wait/notify为什么必须synchronized包裹

首先,jvm内部在notify与wait逻辑中都强制判断是否有对等待对象加锁,所以如下代码逻辑会抛异常

static class BlockingQ {
        private static final int MAX_SIZE = 10;
        private final List<String> queue = new ArrayList<>(MAX_SIZE);
                private final Object putWait = new Object();
                private final Object getWait = new Object();
        public void put(String s) throws InterruptedException {
            synchronized (queue) { //这里锁住的是queue对象
                while (queue.size() >= MAX_SIZE)
                    putWait.wait(); //这里的wait必须是针对queue对象进行wait
                queue.add(s);
                getWait.notifyAll(); //同理
            }
        }

        public String get() throws InterruptedException {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    getWait.wait(); //同理
                }
                String result = queue.remove(0);
                putWait.notifyAll(); //同理
                return result;
            }
        }
    }

修改后的正确代码实现为:

static class BlockingQ {
        private static final int MAX_SIZE = 10;
        private final List<String> queue = new ArrayList<>(MAX_SIZE);

        public void put(String s) throws InterruptedException {
            synchronized (queue) {
                while (queue.size() >= MAX_SIZE)
                    queue.wait();
                queue.add(s);
                queue.notifyAll();
            }
        }

        public String get() throws InterruptedException {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    queue.wait();
                }
                String result = queue.remove(0);
                queue.notifyAll();
                return result;
            }
        }
    }

要实现条件队列细化,必须使用ReentrantLock与condition这对叼逼组合

jvm为什么强制wait/notify必须包裹在synchronized块中

queue.wait();这句话被调用时,jvm会释放自身这把锁,如果没有同步锁或者锁不是自己这个对象,将会报错。
原因-----------摘自stackoverflow

class BlockingQueue {
    Queue<String> buffer = new LinkedList<String>();

    public void give(String data) {
        buffer.add(data);
        notify();                   // Since someone may be waiting in take!
    }

    public String take() throws InterruptedException {
        while (buffer.isEmpty())    // don't use "if" due to spurious wakeups.
            wait();
        return buffer.remove();
    }
}

This is what could potentially happen:

1.A consumer thread calls take() and sees that the buffer.isEmpty().
2.Before the consumer thread goes on to call wait(), a producer thread comes along and invokes a full give(), that is, buffer.add(data); notify();
3.The consumer thread will now call wait() (and miss the notify() that was just called).
4.If unlucky, the producer thread won't produce more give() as a result of the fact that the consumer thread never wakes up, and we have a dead-lock.

Once you understand the issue, the solution is obvious: Always perform give/notify and isEmpty/wait atomically.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容