给出以下Java代码:
public class Test {
static private class MyThread extends Thread {
private boolean mustShutdown = false;
@Override
public synchronized void run() {
// loop and do nothing, just wait until we must shut down
while (!mustShutdown) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("Exception on wait()");
}
}
}
public synchronized void shutdown() throws InterruptedException {
// set flag for termination, notify the thread and wait for it to die
mustShutdown = true;
notify();
join(); // lock still being held here, due to 'synchronized'
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
try {
Thread.sleep(1000);
mt.shutdown();
} catch (InterruptedException e) {
System.out.println("Exception in main()");
}
}
}
运行它将等待一秒然后正确退出。但这对我来说意外,我预计会发生死锁。
我的推理如下:新创建的MyThread将执行run(),它被声明为'synchronized',因此它可以调用wait()并安全地读取'mustShutdown';在wait()调用期间,锁定被释放并在返回时重新获取,如wait()文档中所述。一秒钟后,主线程执行shutdown(),再次同步,以便在其他线程正在读取的同时不访问mustShutdown。然后它通过notify()唤醒另一个线程,并通过join()等待它的完成。
但在我看来,其他线程无法从wait()返回,因为它需要在返回之前重新获取线程对象上的锁。它不能这样做因为shutdown()在join()内部仍然保持锁定。为什么它仍能正常工作并正常退出?
答案 0 :(得分:8)
join()方法在内部调用 wait(),这将导致释放锁定(Thread对象)。
请参阅下面的join()代码:
public final synchronized void join(long millis)
throws InterruptedException {
....
if (millis == 0) {
while (isAlive()) {
wait(0); //ends up releasing lock
}
}
....
}
你的代码看到这个并且一般看不到的原因::你的代码看到这个而不是一般没有观察到的原因是因为join()方法等待<() strong>线程对象本身,因此放弃对Thread对象本身的锁定,并且当run()方法也在同一个Thread对象上同步时,您会看到这种意外情况。
答案 1 :(得分:1)
Thread.join的实现使用wait,这使得它可以锁定,这就是为什么它不会阻止其他线程获取锁。
以下是对此示例中发生的事情的逐步说明:
在main方法中启动MyThread线程会导致执行MyThread run方法的新线程。主线程休眠一整秒,让新线程有足够的时间启动并获取MyThread对象的锁定。
然后新线程可以进入wait方法并释放其锁定。此时新线程进入休眠状态,在被唤醒之前不再尝试获取锁定。线程还没有从wait方法返回。
此时主线程从休眠状态唤醒,并在MyThread对象上调用shutdown。获取锁没有问题,因为新线程一旦开始等待就会释放它。主线程现在调用notify。进入join方法,主线程检查新线程是否仍处于活动状态,然后等待,释放锁定。
主线程释放锁定后会发生通知。由于新线程在主线程调用notify时处于锁定的等待集中,因此新线程接收通知并唤醒。它可以获取锁,离开wait方法,并完成执行run方法,最后释放锁。
新线程的终止会导致等待其锁定的所有线程都收到通知。这会唤醒主线程,它可以获取锁并检查新线程是否已死,然后它将退出join方法并完成执行。
/**
* Waits at most <code>millis</code> milliseconds for this thread to
* die. A timeout of <code>0</code> means to wait forever.
*
* @param millis the time to wait in milliseconds.
* @exception InterruptedException if any thread has interrupted
* the current thread. The <i>interrupted status</i> of the
* current thread is cleared when this exception is thrown.
*/
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;
}
}
}
答案 2 :(得分:0)
补充其他答案:我没有看到join()
在API文档中发布任何锁定,所以这种行为实际上是特定于实现的。
从中学习:
Thread
,而是使用传递给线程对象的Runnable
实现。