我有这样的代码:
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)实现,似乎线程在调用isAlive
和wait
之间完成(在源代码中标记):
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,但行为是相同的(它挂起)。
所以我的问题是,如果我对竞争条件是真实的,以及如何在没有任何悬挂的情况下明确地加入线程......
由于
答案 0 :(得分:2)
所以我的问题是,如果我对竞争条件是真实的,以及如何在没有任何悬挂的情况下明确地加入线程......
该方法没有比赛。由于方法为synchronized
,因此解决了竞争条件。如果它是一个竞争条件,那么之前大量的Java线程可能会失败。我怀疑你加入的主题仍在运行。这不可能吗?
问题是,在某些情况下,连接挂起 - jstack输出是这样的:
此堆栈输出显示它在join()
中阻塞。正如@Holger所提到的那样,你能看到t1
或t2
堆栈吗?命名线程可能很有用,这样您就可以在堆栈跟踪输出中更容易地看到它们:
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()
中的阻止相同的行为。