等待/通知的奇怪java行为
Posted
技术标签:
【中文标题】等待/通知的奇怪java行为【英文标题】:Strange java behavior of wait/notify 【发布时间】:2014-04-10 21:06:55 【问题描述】:我发现了 java 并发的奇怪行为。请参阅下面的下一个代码:
公共类测试 静态 CountDownLatch 锁存器 = 新的 CountDownLatch(1); public static void main(String[] args) 抛出 UnsupportedEncodingException, InterruptedException 最终线程 t = new MyThread(); t.start(); 同步(t) 锁存器.countDown(); System.out.println("要睡觉了"); t.wait(); System.out.println("唤醒"); 静态类 MyThread 扩展 Thread @覆盖 公共无效运行() 尝试 锁存器.await(); 捕捉(InterruptedException e) e.printStackTrace(); 同步(这个) System.out.println("内部运行"); // notifyAll();在我看来,这段代码应该挂断并永远等待,但是代码已经完成,控制台中的 next out 没有任何问题:
睡觉了 内跑 醒来我试图找到一些关于在线程死亡时通知锁的信息,但缺少它。 我也没有在 java 规范中找到任何信息。
但是,如果我尝试锁定其他对象(而不是线程对象),它可以正常工作。
【问题讨论】:
你锁定线程实例上的同步,运行线程,然后在 Main 中等待线程。在这一点上,线程应该有统治权。它在 this 上同步,这与 Main 等待的相同,然后调用 notifyAll(),它释放 Main 中的等待。该应用程序应该可以正常运行。 另外,not 在线程上调用 wait/notify/notifyAll - 它在内部使用它。哦,更喜欢实现 Runnable 而不是扩展 Thread :) 为什么 notifyAll() 被注释掉了?如果它处于活动状态,那么我也希望代码不会挂断。 【参考方案1】:这是因为您正在等待 Thread
实例。 Thread
在内部使用wait
/notify
/notifyAll
,所以你也不应该自己这样做——你会混淆Thread
,而Thread
会混淆你。特别是当一个线程退出时,它会调用this.notifyAll()
。
来自Thread.join
的文档:
此实现使用以
this.isAlive
为条件的this.wait
调用循环。当一个线程终止时,this.notifyAll
方法被调用。建议应用程序不要在Thread
实例上使用wait
、notify
或notifyAll
。
一般来说,尝试锁定并等待 nothing else 可以与之交互的对象。这样您就可以推断对象上存在的与并发相关的操作,因为它们非常有限。只要任意代码可以在对象上同步(并调用等待/通知等),就很难证明您的代码是正确的。这就是为什么您会经常看到类似的内容:
public class Foo
private final Object lock = new Object();
... code which uses synchronized(lock) ...
【讨论】:
感谢您的回答。我知道这是不好的做法。这就像java拼图。我的朋友问过这个问题,我无法解释为什么它不起作用。 @EvgeniyKiselev:嗯,希望现在你可以:) 将来,我会在问题中添加一些上下文指示。在这种情况下问题不大,但通常会有所帮助。 即使它不是线程实例也可能发生这种情况,因为允许虚假唤醒。【参考方案2】:正如 Jon Skeet 所写,在 Thread 对象上同步是个坏主意,请使用另一个对象:
public class Test
static CountDownLatch latch = new CountDownLatch(1);
static final Object sync = new Object();
public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException
final Thread t = new MyThread();
t.start();
synchronized (sync)
latch.countDown();
System.out.println("got to sleep");
sync.wait();
System.out.println("wake up");
static class MyThread extends Thread
@Override
public void run()
try
latch.await();
catch (InterruptedException e)
e.printStackTrace();
synchronized (sync)
System.out.println("inside run");
//sync.notifyAll();
现在这段代码永远不会结束,直到你取消注释 sync.notifyAll();
【讨论】:
我知道这是不好的做法,而且我知道如果锁定不同的对象,它会正常工作。以上是关于等待/通知的奇怪java行为的主要内容,如果未能解决你的问题,请参考以下文章
Java并发编程之十二:线程间通信中notifyAll造成的早期通知问题(含代码)
转:Java并发编程之十二:线程间通信中notifyAll造成的早期通知问题(含代码)