在多线程里面,为什么推荐使用notifyAll而不是notify

发布时间 2023-10-30 16:11:05作者: 深夜小码农

在多线程里面,为什么推荐使用notifyAll而不是notify?


结论:notify容易造成死锁

1、无论使用notify或者notifyAll,都是随机唤醒线程

2、notify是随机唤醒一个线程去执行,noifyAll是唤醒所有线程,然后去抢占锁,去执行

怎么产生死锁现象:

P – 生产者 调用 putproduct C – 消费者 调用 consumer。

  1. P2 调用product,发现满了,在wait里面等了。

  2. P2 调用product,发现满了,在wait里面等了。

  3. P2 调用product,发现满了,在wait里面等了。

  4. C1 想来拿,C2,C3 就在 consumer里面等着。

  5. C1 开始执行,移除remove,然后调用 notify 然后退出。

  • 如果 C1 把 C2 唤醒了,所以P2 (其他的都得等)只能在product方法上等着。
  • C2 检查 while 循环发现此时队列是空的,所以就在 wait 里面等着。
  • C3 也比 P2 先执行,那么发现也是空的,只能等着了。
  1. 这时候我们发现 P2、C2、C3 都在等着锁,最终 P2 拿到了锁,放一个 1,notify,然后退出。

  2. P2 这个时候唤醒了P3,P3发现队列是满的,没办法,只能等它变为空。 8. 这时候没有别的调用了,那么现在这三个线程(P3, C2,C3)就全部变成 suspend 了,也就是死锁了。

代码示例
public class Thread03{

    private Buffer mbuffer = new Buffer();
    // private List<Object> list = new ArrayList<>();

    public void product(){
        synchronized (this) {
            while (mbuffer.isFull()){
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            mbuffer.add();
            notify();
        }

    }

    public void consumer(){
        synchronized (this) {
            while (mbuffer.isEmpty()){
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            mbuffer.remove();
            notify();
        }

    }

    private class Buffer {
        private static final int MAX_CAPACITY = 1;
        private List innerList = new ArrayList<>(MAX_CAPACITY);

        void add() {
            if (isFull()) {
                throw new IndexOutOfBoundsException();
            } else {
                innerList.add(new Object());
            }
            System.out.println(Thread.currentThread().toString() + " add");

        }

        void remove() {
            if (isEmpty()) {
                throw new IndexOutOfBoundsException();
            } else {
                innerList.remove(MAX_CAPACITY - 1);
            }
            System.out.println(Thread.currentThread().toString() + " remove");
        }

        boolean isEmpty() {
            return innerList.isEmpty();
        }

        boolean isFull() {
            return innerList.size() == MAX_CAPACITY;
        }
    }

    public static void main(String[] args) {
        Thread03 thread03 = new Thread03();
        Runnable product = new Runnable() {
            int count = 4;

            @Override
            public void run() {
                while (count-- > 0) {
                    thread03.product();
                }

            }
        };

        Runnable consumer = new Runnable(){
            @Override
            public void run() {
                int count = 4;
                while (count-- >0){
                    thread03.consumer();
                }            }
        };

        for (int i = 0; i < 2; i++) {
            new Thread(product).start();
        }

        for (int i = 0; i < 2; i++) {
            new Thread(consumer).start();
        }
    }

}