同步后,wait / notifyAll必须在同一对象上,但是为什么呢?

时间:2019-03-30 08:16:22

标签: java java-8 wait synchronized

当尝试使用wait()synchronized进行简单演示时,发生了一件有趣的事情,以下演示为我提供了意料之外的输出。

public class WaitZero {
    private static AtomicInteger num = new AtomicInteger(0);
    private static boolean consumed = false;

    public static void main(String... args) throws Exception {
        ThreadPoolExecutor threadPoolExecutor = getMyCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            threadPoolExecutor.submit(WaitZero::send);
            threadPoolExecutor.submit(WaitZero::receive);
        }
        threadPoolExecutor.shutdown();
        threadPoolExecutor.awaitTermination(60, TimeUnit.SECONDS);
    }

    private static synchronized void send() {
        try {
            while (!isConsumed()) {
                num.wait();
            }
        } catch (InterruptedException ignored) {
            ignored.printStackTrace();
        }
        num.incrementAndGet();
        System.out.println(Thread.currentThread().getName() + " number updated: " + num);
        setConsumed(false);
        num.notifyAll();
    }

    private static synchronized void receive() {
        try {
            while (isConsumed()) {
                num.wait();
            }
        } catch (InterruptedException ignored) {
            ignored.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " number received: " + num);
        setConsumed(true);
        num.notifyAll(); // ToDo: when to use notify?
        // ToDo: what is monitor?
    }

    private static boolean isConsumed() {
        return consumed;
    }

    private static void setConsumed(boolean consumed) {
        WaitZero.consumed = consumed;
    }
}

它的输出不稳定,但是其中一种典型的可能是

shared-pool-0 number received: 0
shared-pool-1 number updated: 1
shared-pool-0 number received: 1
shared-pool-1 number updated: 2
shared-pool-1 number received: 2
shared-pool-2 number updated: 3

虽然我期望的是

shared-pool-1 number received: 0
shared-pool-0 number updated: 1
shared-pool-3 number received: 1
shared-pool-2 number updated: 2
shared-pool-1 number received: 2
shared-pool-0 number updated: 3
shared-pool-2 number received: 3
shared-pool-3 number updated: 4
shared-pool-5 number received: 4
shared-pool-4 number updated: 5

当我在WaitZero.class上使用num而不是wait()/notifyAll()时,将检索到正确的结果。

我已经看过书了,看来总是在同一对象上使用其中三个来确保正确性的情况。

我的猜测:如果不是所有的对象都在同一个对象上,则notifyAll()与同步锁之间有一种特殊情况。那是什么

任何帮助将不胜感激;)

1 个答案:

答案 0 :(得分:0)

在@JB Nizet,@ Amardeep Bhowmick和所有人的许多幼稚问题和大力帮助之后,我发现How to work with wait(), notify() and notifyAll() in Java?的精妙句子准确地解释了原因。

wait()方法被设计/用于放弃锁(由于不满足某些条件)以让其他线程工作/合作;典型的用例是发送者/接收者或生产者/消费者。

  

wait()

     

它告诉调用线程放弃并进入睡眠状态,直到其他线程进入同一监视器并调用notify()...。实际上,wait()方法与同步锁紧密集成在一起,使用的同步功能无法直接从同步机制获得。

synchronized(lockObject) {
    while( ! condition ) {
        lockObject.wait();
    }
    //take the action here;
}

在这种情况下,可以按照以下说明解决问题,或者仅将WaitZero.class用于wait/notifyAll

public class WaitZero {
    private static AtomicInteger num = new AtomicInteger(0);
    private static boolean consumed = false;

    public static void main(String... args) throws Exception {
        ThreadPoolExecutor threadPoolExecutor = getMyCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            threadPoolExecutor.submit(WaitZero::send);
            threadPoolExecutor.submit(WaitZero::receive);
        }
        threadPoolExecutor.shutdown();
        threadPoolExecutor.awaitTermination(60, TimeUnit.SECONDS);
    }

    private static void send() {
        synchronized (num) {
            try {
                while (!isConsumed()) {
                    num.wait();
                }
            } catch (InterruptedException ignored) {
                ignored.printStackTrace();
            }
            num.incrementAndGet();
            System.out.println(Thread.currentThread().getName() + " number updated: " + num);
            setConsumed(false);
            num.notifyAll();
        }
    }

    private static void receive() {
        synchronized (num) {
            try {
                while (isConsumed()) {
                    num.wait();
                }
            } catch (InterruptedException ignored) {
                ignored.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " number received: " + num);
            setConsumed(true);
            num.notifyAll(); // ToDo: when to use notify?
            // ToDo: what is monitor?
        }
    }

    private static boolean isConsumed() {
        return consumed;
    }

    private static void setConsumed(boolean consumed) {
        WaitZero.consumed = consumed;
    }
}