当尝试使用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()
与同步锁之间有一种特殊情况。那是什么
任何帮助将不胜感激;)
答案 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;
}
}