繁体   English   中英

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

[英]synchronized, wait/notifyAll has to be on the same object, but why?

在尝试使用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

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

我已经阅读过,似乎总是必须在同一个对象上使用其中三个以确保正确性。

我的猜测:如果不是所有这些都在同一个对象上,那么notifyAll()和同步锁之间存在特殊情况。 但它是什么?

任何帮助将不胜感激 ;)

在@JB Nizet、@Amardeep Bhowmick 等提出了许多幼稚的问题和大力帮助之后,我从Java 中如何使用 wait()、notify() 和 notifyAll() 中找到了一个简洁的句子 准确说明原因。

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;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM