美文网首页
wait/notify为什么必须synchronized包裹

wait/notify为什么必须synchronized包裹

作者: 炫迈哥 | 来源:发表于2017-04-19 21:39 被阅读0次

首先,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.

相关文章

网友评论

      本文标题:wait/notify为什么必须synchronized包裹

      本文链接:https://www.haomeiwen.com/subject/qvjuattx.html