等待线程是否重新访问synchronized方法中的代码

时间:2014-08-24 18:06:42

标签: java multithreading wait

我正在阅读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
}

2 个答案:

答案 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()

时打印一次