wait/notify为什么必须synchronized包裹

2017-04-19  本文已影响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.

上一篇 下一篇

猜你喜欢

热点阅读