我正在阅读tutorial中的线程同步和等待/通知构造。它说明了
当调用wait时,线程释放锁并暂停执行。在将来的某个时间,另一个线程将获取相同的锁并调用Object.notifyAll,通知等待该锁的所有线程发生了重要的事情。
在第二个线程释放锁之后的一段时间,第一个线程重新获取锁并通过从等待调用返回来恢复。
AFAIK,如果有多个线程在第一个线程被notify
唤醒时可以竞争锁定,那么它们中的任何一个都可以拥有该对象的锁定。我的问题是,如果第一个线程本身重新获取锁,它是否必须从synchronized方法的开头全部开始(这意味着它再次在while循环检查wait()条件之前执行代码)或者它只是暂停在wait()
行?
// Does the waiting thread come back here while trying to own the
// lock (competing with others)?
public synchronized notifyJoy() {
// Some code => Does this piece of code gets executed again then in case
// waiting thread restarts its execution from the method after it is notified?
while (!joy) {
try {
// Does the waiting thread stay here while trying to re-acquire
// the lock?
wait();
} catch(InterruptedException e) {}
}
// Some other code
}
答案 0 :(得分:8)
只有当执行它的线程完成执行其run方法时才会退出方法,无论是通过正常返回还是抛出在该run方法中未被捕获的异常。你的方法不的唯一方法就是在上面发生的事情之一被执行之后,JVM就会从你身下被杀死(使用java.lang.System.exit,杀死java进程) kill -9等),或者如果该方法正在JVM正在关闭的守护程序线程中运行。这里没有什么奇怪的事情发生。等待的线程放弃锁定并进入休眠状态,但它不会以某种方式停止执行该方法。
从等待呼叫唤醒的线程从未到过任何地方;线程等待的整个时间仍然在等待方法中。在它离开等待方法之前,首先必须获得它放弃的锁以便开始等待。然后它需要重新测试它需要检查的任何条件才能知道是否继续等待。
这就是为什么the guarded blocks tutorial告诉你等待必须在循环中完成的原因:
在另一个线程发出可能发生某些特殊事件的通知之前,等待的调用不会返回 - 尽管不一定是该线程正在等待的事件:
public synchronized void guardedJoy() {
// This guard only loops once for each special event, which may not
// be the event we're waiting for.
while(!joy) {
try {
wait();
} catch (InterruptedException e) {}
}
System.out.println("Joy and efficiency have been achieved!");
}
注意:始终在测试等待条件的循环内调用wait。不要假设中断是针对您正在等待的特定条件,或者条件仍然是真的。
(本教程使用的措辞具有误导性;“中断”一词应为“通知”。另外不幸的是,显示的教程代码在没有设置中断标志的情况下吃了InterruptedException,最好让InterruptedException从这种方法抛出而根本没有抓住它。)
如果线程确实“重新开始”,则不需要此循环;你的代码将从方法的开头开始,获取锁,并测试正在等待的条件。
答案 1 :(得分:0)
线程执行在等待调用之后直接启动。它不会从头重新启动块。 wait()可以大致实现类似于
public void wait() {
release_monitor();
wait_monitor();
acquire_monitor();
}
这不是实际实现的地方,它只是对幕后发生的事情的粗略概念。每个对象都有一个与之关联的监视器,可以获取和释放。一次只有一个线程可以容纳监视器,并且线程可以递归地获取监视器而没有问题。对Object的等待调用释放监视器,允许另一个线程获取它。等待线程然后等待,直到通过通知/ notifyAll唤醒它。在被唤醒后,等待的线程再次等待需要对象的监视器并返回到调用代码。
示例:
private Object LOCK = new Object;
private int num = 0;
public int get() {
synchronized( LOCK ) {
System.out.println( "Entering get block." );
LOCK.wait();
return num;
}
}
public void set( int num ) {
synchronized( LOCK ) {
System.out.println( "Entering set block." );
this.num = num;
LOCK.notify();
}
}
"Entering get block."
只会在每次调用get()