1

我有这样的代码:

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)

并且根据joinJDK(我使用 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 进行了同样的尝试,但行为是相同的(它挂起)。

所以我的问题是,如果我对比赛条件是真的,以及如何在没有任何挂起的情况下清楚地加入线程......

谢谢

4

1 回答 1

2

所以我的问题是,如果我对比赛条件是真的,以及如何在没有任何挂起的情况下清楚地加入线程......

不,这种方法没有比赛。由于方法是synchronized. 如果这是一个竞争条件,那么大量的 Java 线程可能在此之前就已经失败了。我怀疑您要加入的线程仍在运行。那不可能吗?

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

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

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()) {

如果第 3 方库没有重新启用中断位(如果它捕获),您还需要工作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()

于 2013-10-18T17:19:25.143 回答