Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java wait()/join():为什么这不会死锁?_Java_Multithreading_Join_Wait_Notify - Fatal编程技术网

Java wait()/join():为什么这不会死锁?

Java wait()/join():为什么这不会死锁?,java,multithreading,join,wait,notify,Java,Multithreading,Join,Wait,Notify,给定以下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

给定以下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()内时仍持有锁。为什么它仍然正常工作并退出?

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;
    }
}
}
join()方法在内部调用wait(),这将导致释放(线程对象的)锁

请参见下面join()的代码:


代码通常看到而没有看到的原因::代码通常看不到而没有看到的原因是join()方法等待线程对象本身,从而放弃对线程对象本身的锁定,并将其作为您的运行()方法也在同一个线程对象上同步,您会看到这一意外情况。

为了补充其他答案:我在API文档中没有提到释放任何锁的
join()
,因此此行为实际上是特定于实现的

从中学习:

  • 不要子类化
    线程
    ,而是使用传递给线程对象的
    可运行
    实现
  • 不要对您不“拥有”的对象进行同步/等待/通知,例如,您不知道还有谁可能对其进行同步/等待/通知

这确实解释了这一点。但正如帕罗·埃伯曼所指出的那样,文件中并没有提到这一点。我可以依靠join()释放锁吗?当然可以。自从我进入青春期,代码就没有改变。不用担心。@jlh再想想,peterlawrey的评论也有道理……那就是“实现一个用线程包装的Runnable”,我会看一看,谢谢。“extend thread implement runnable java”是非常愚蠢的。我有一个疑问。从shutdown方法调用notify时,锁将被释放,因此不存在join-keeping-lock的问题,因为只有当它再次获得锁时才会执行。我是对的还是遗漏了什么?在这种特定情况下,实现Runnable而不是子类化线程对我来说似乎更复杂。。。只有MyThread使用过“synchronized”、wait()、join()和notify()。主类从不使用它。(虽然主线程确实如此。)所以我不确定您的第二个要点。重点是
join
在内部使用
wait()
,这是您可能没有想到的。例如,
MyThread
对象的监视器用于两个目的:同步您自己的运行/关闭周期,以及同步内部线程管理。从Java 7开始,API规范join(long)规定:“此实现使用This.wait调用的循环,条件是This.isAlive。当线程终止时,调用this.notifyAll方法。“。因为涉及等待,所以也涉及锁。Paulo的回答一定是基于Java 6 API,因为Java 7当时刚刚发布。因为这样的副作用,直接扩展线程是不可取的。你应该实现一个Runnable,用线程包装它。
public final synchronized void join(long millis) 
    throws InterruptedException {
    ....
    if (millis == 0) {
       while (isAlive()) {
         wait(0);  //ends up releasing lock
       }
    }
    ....
}