如果线程正在完成,Java Thread.join()方法会冻结

时间:2013-10-18 17:00:40

标签: java multithreading

我有这样的代码:

public void testThreads() throws Exception {
   MyThreadManager mgr = createAndStartMyManager();
   Thread t1 = createMyThread(mgr, 1);
   Thread t2 = createMyThread(mgr, 2);

   t1.start();
   t2.start();

   //do some checks

   t1.join();
   t2.join();
}

MyThreadManager可以使用线程进行操作,可以随时中断其中任何一个。 因为这是测试用例的一部分,我需要调试正在运行的线程,所以我添加了joins让测试等待完成我的调试(JUnit在测试完成后断开调试器的连接)。

问题是,在某些情况下,连接挂起 - jstack输出是这样的:

    at java.lang.Object.wait(Native Method)
    - waiting on <0xeec68eb0> (a java.lang.Thread)
    at java.lang.Thread.join(Thread.java:1143)
    - locked <0xeec68eb0> (a java.lang.Thread)
    at java.lang.Thread.join(Thread.java:1196)
    at MyClass.testThreads(MyClass.java:100)

并且根据join JDK(我使用1.6.0_07)实现,似乎线程在调用isAlivewait之间完成(在源代码中标记):

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()) {
            // -----> here ???? <------
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

我尝试使用ThreadPoolExecutor和FutureTask,但行为是相同的(它挂起)。

所以我的问题是,如果我对竞争条件是真实的,以及如何在没有任何悬挂的情况下明确地加入线程......

由于

1 个答案:

答案 0 :(得分:2)

  

所以我的问题是,如果我对竞争条件是真实的,以及如何在没有任何悬挂的情况下明确地加入线程......

该方法没有比赛。由于方法为synchronized,因此解决了竞争条件。如果它是一个竞争条件,那么之前大量的Java线程可能会失败。我怀疑你加入的主题仍在运行。这不可能吗?

  

问题是,在某些情况下,连接挂起 - jstack输出是这样的:

此堆栈输出显示它在join()中阻塞。正如@Holger所提到的那样,你能看到t1t2堆栈吗?命名线程可能很有用,这样您就可以在堆栈跟踪输出中更容易地看到它们:

Thread t1 = createMyThread(mgr, 1, "t1"); // name passed to thread constructor
Thread t2 = createMyThread(mgr, 2, "t2"); // name passed to thread constructor
  

MyThreadManager使用线程运行并可能中断

你打电话给thread.interrupt()吗?如果是这样,除非您正在测试线程中断位和/或正确处理InterruptedException,否则这不会导致线程停止。

while (!Thread.currentThread().isInterrupted()) {

如果第三方库在捕获InterruptedException时未重新启用中断位,则还需要工作。正确的模式是做类似的事情:

try {
    ...
} catch (InterruptedException ie) {
    // re-enable interrupt bit since catching InterruptedException clears it
    Thread.currentThread().interrupt();
    // handle the interrupt by maybe quitting the thread
    return;
}
  

我尝试使用ThreadPoolExecutor和FutureTask,但行为是相同的(它挂起)。

使用TPE,您需要确保在提交所有作业后关闭执行程序。这将导致您的应用程序挂起,但不会显示与join()中的阻止相同的行为。