【发布时间】:2019-08-21 02:36:00
【问题描述】:
当我尝试使用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() 和同步锁之间存在特殊情况。但它是什么?
任何帮助将不胜感激;)
【问题讨论】:
-
你的方法是同步的,因为它们是静态的,这意味着它使用方法的封闭类的监视器。这就是当您在
WaitZero.class上调用wait/notify/notifyAll方法时它起作用的原因,因为您在同一个Class对象上进行同步。它不适用于num,因为您从不同步它——也不应该因为它是一个非最终的、积极变化的参考。 -
wait 和 notifyAll 都要求线程持有锁监视器。他们从不这样做,因此总是抛出 IllegalMonitorStateException。并且您通过使用 num++ 继续为 num 分配一个新值,因此您甚至不会在同一个对象上调用 wait 和 notifyAll 。 1.不要使用像0这样的共享值作为锁。使用您自己创建的私有最终锁定对象。 2.总是同步、等待和通知锁。 3. 首先避免这些低级、容易出错的同步原语。使用来自 java.util.concurrent 的更高级别的抽象。
-
@Hearen Integer 是不可变的,因此当您执行
num++时,您正在创建一个具有递增值的新 Integer 实例。所以对象发生了变化。 -
没有。
num++等价于num = Integer.valueOf(num.intValue() + 1)。它将另一个对象分配给num变量。0是一个共享的缓存整数值。因此任何其他线程也可能在其上同步,从而导致干扰、死锁等。 -
同样,wait 和 notifyAll 都要求线程持有锁监视器。他们从不这样做,因此总是抛出 IllegalMonitorStateException。
标签: java java-8 wait synchronized