Java 为什么';t线程等待notify()?
为什么线程不等待Java 为什么';t线程等待notify()?,java,concurrency,wait,notify,Java,Concurrency,Wait,Notify,为什么线程不等待notify()?线程启动后进入等待池,但在那一刻之后继续执行 public class JavaApplication2 { public static void main(String [] args) { ThreadB b = new ThreadB(); synchronized(b) { b.start(); try { System.out.println("
notify()
?线程启动后进入等待池,但在那一刻之后继续执行
public class JavaApplication2 {
public static void main(String [] args) {
ThreadB b = new ThreadB();
synchronized(b) {
b.start();
try {
System.out.println("1");
b.wait();
} catch (InterruptedException e) {}
System.out.println("Total is: " + b.total);
}
}
}
class ThreadB extends Thread {
int total;
@Override
public void run() {
synchronized(this) {
total += 1;
//notify();
}
}
}
您不能指望等待返回,直到收到通知:“可能会出现中断和虚假唤醒”。通常,当线程继续等待时,您应该将等待调用包装在循环中。您正在线程对象本身上进行同步,这是错误的用法。发生的情况是,即将死亡的执行线程总是在其
线程对象上调用通知:线程.join
。因此,很明显,为什么在有和没有自己的notify
的情况下都会出现相同的行为
解决方案:使用单独的对象进行线程协调;这是标准做法。在这两个地方嵌套了同步的{}构造。这些构造似乎在做一些奇怪的事情:线程根本不会对notify作出反应,只有在ThreadB(b)终止时才会恢复。删除此项:
public class JavaApplication2 {
public static void main(String[] args) {
ThreadB b = new ThreadB();
b.start();
try {
System.out.println(" ### Waiting for notify");
synchronized (b) {
b.wait();
}
System.out.println(" ### Notified");
} catch (InterruptedException e) {
}
System.out.println("### Total is: " + b.total);
}
}
class ThreadB extends Thread {
int total;
@Override
public void run() {
total += 1;
System.out.println(" *** Ready to notify in 5 secs");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(" *** Notification sent");
synchronized (this) {
notify();
}
System.out.println(" *** 5 sec post notification");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(" *** ThreadB exits");
}
}
上面的代码可能工作正常:当notify()出现时,主线程在5秒钟后,在我们看到ThreadB终止的消息之前恢复。注释掉notify()后,主线程将在10秒后恢复,并在关于线程终止的消息后恢复,因为notify()无论如何都会从其他代码中调用。Marko Topolnik解释了此“幕后”notify()调用的来源和原因。如果您尝试在任何对象上同步代码,而不是在ThreadB
上,您将发现它从未终止。这是因为存在对notify
的隐藏调用
虽然我不知道在什么地方指定了它,但线程在结束时会通知自己。这在join
方法的实现方式中是隐含的。这是加入的代码:
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
公共最终同步无效联接(长毫秒)
抛出中断异常{
长基=System.currentTimeMillis();
long now=0;
如果(毫秒<0){
抛出新的IllegalArgumentException(“超时值为负”);
}
如果(毫秒==0){
while(isAlive()){
等待(0);
}
}否则{
while(isAlive()){
长延迟=毫秒-现在;
如果(delay为终止线程的线程
对象调用了方法notifyAll()
。在的描述中奇怪地记录了这一事实,并使用以下语句:
当线程终止时,将调用this.notifyAll方法。建议应用程序不要在线程实例上使用wait、notify或notifyAll
因此,如果你没有明确阅读join
的描述(你不必阅读),你就不会知道奇怪行为的原因。在阅读OCP SE 7时,我在等待/通知操作上做了相同的测试,很好。我想我们应该让作者解释一下。什么通知?我不明白任何通知,除了注释掉的通知。确切地说,线程必须等待永恒,但它不会。tryb.join()
而不是等待
这并不能回答问题。是的,虚假唤醒只是一个次要问题,许多实现根本没有表现出来。无论你在哪里遇到确定性可复制行为,你都可以100%确定虚假唤醒与此无关。完全同意@Marko。我真的不喜欢menti错误的唤醒。它们当然是可能的,但极为罕见。很可能是try bug的错误。请参阅以下错误答案:+1,用于使用单独的线程进行协调。如果您不知道系统的其他部分对对象做了什么,那么您就不能依赖它进行同步。您是正确的,但不是notify
,调用了this.notifyAll
方法。我不理解此注释。notify
与this相同。notify
notifyAll
与notify
不同。它们是不同的方法。一般来说,它们不相同,但在您的情况下(等待集中只有一个线程),是的。您似乎使用“伪唤醒”这个术语太过松散了——这是一个非常具体的概念,它不涉及这个问题(或我所见过的任何其他问题)。当线程b终止时,主线程确实会恢复,即使notify()也不例外没有调用,已测试。当然;notify
无论如何都会调用,正如我在回答中所解释的。这不是虚假的唤醒。也可能看起来如此……我在回答中更改了这句话。