程序创建线程t0,它产生线程t1,然后创建线程t2和t3。执行线程t3
后,应用程序永远不会返回到之前生成的其他线程(t0,t1,t2)和他们被困住了。
为什么线程t0
,t1
和t2
被暂停?
public class Cult extends Thread
{
private String[] names = {"t1", "t2", "t3"};
static int count = 0;
public void run()
{
for(int i = 0; i < 100; i++)
{
if(i == 5 && count < 3)
{
Thread t = new Cult(names[count++]);
t.start();
try{
Thread.currentThread().join();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName() + " ");
}
}
public static void main(String[] a`)
{
new Cult("t0").start();
}
}
答案 0 :(得分:2)
你错过的最重要的一点:
Thread.currentThread().join();
源代码中的方法join
使用isAlive
方法。
public final synchronized void join(long millis)
...
if (millis == 0) {
while (isAlive()) {
wait(0);
}
...
}
这意味着Thread.currentThread().join()
仅在Thread.currentThread()
失效时才会返回。
但是在你的情况下,由于Thread.currentThread()
中运行的代码本身就是不可能的
这种代码的和平Thread.currentThread().join()
。这就是为什么在线程3完成后你的程序应该挂起并且之后没有任何事情发生。
答案 1 :(得分:1)
为什么线程t0,t1和t2暂停?线程t3的执行完成。
t3
完成,因为它不尝试分叉第4个线程,因此不会尝试join()
使用它自己的线程。以下行永远不会返回,所以t0,t1和t2都停在那里并永远等待:
Thread.currentThread().join();
这是要求当前线程等待本身完成哪个不起作用。我怀疑你是想说t.join();
等待刚刚分叉完成的线程。
以下是关于您的代码的一些其他想法,没有明显的顺序:
您应该考虑implements Runnable
而不是extends Thread
。见这里:"implements Runnable" vs. "extends Thread"
您在多个线程中使用共享的static
变量count
,没有任何锁定保护。最佳解决方案是使用AtomicInteger
而不是int
。你可能在这里没有问题,因为每个线程都在修改count
,然后分叉另一个线程但是如果你试图分叉2个线程,由于数据竞争条件,这将是一个真正的问题。
我不确定你为什么只生成另一个帖子if(i == 5 && count < 3)
。 i
在该循环中只有5
一次。这真的是你想要的吗?
String[] names = {"t1", "t2", "t3"};
个字段。否则他们会被埋没在代码中并迷失。
在main
中,您启动Cult
线程,然后主线程结束。这是不必要的,您只需在cult.run();
中调用main
,然后使用主线程。
Cult(String s) { super(s); }
让构造函数调用具有相同参数的超级构造函数是没有意义的。这可以删除。
这是值得商榷的,但我倾向于将main
方法置于班级的顶端,而不是埋葬它,因为它是“入口”方法。构造函数也是如此。那些应该高于 run()
方法。
catch(Exception e) {}
是真正的错误模式。至少你应该做e.printStackTrace();
或以某种方式记录它。捕获和删除异常会隐藏很多的问题。此外,应将Exception
更改为catch(InterruptedException e)
。您希望限制您的catch块只是块抛出的异常,否则如果您在某处复制并粘贴该块,这可能会再次隐藏问题。
这是一个很好的做法,但从不使用像3
这样的常量来匹配另一个数据项。在这种情况下,最好使用names.length
,这是3.这意味着如果要增加线程数,则不需要在代码中更改2个位置。您也可以将名称设为"t" + count
并完全删除names
数组。