在下面的代码中,主线程将调用join()
,主线程将等待子线程完成并打印结果9
。
我的问题是join()
会让其他线程等到此线程完成。但是,主线程将调用join
,但为什么它会等到子线程完成???
public class Starter extends Thread{
private int x = 2;
public static void main(String[] args) throws Exception{
new Starter().makeItSo();
}
public Starter(){
x = 5;
start();
}
public void makeItSo() throws Exception{
join(); // ??????
x = x - 1;
System.out.println(x);
}
public void run(){x *= 2;}
}
以下代码是我对join()
的理解,主线程将等到t完成。
Thread t = new Thread(new ThreadDemo());
// this will call run() function
t.start();
// waits for this thread to die
t.join();
System.out.println("Hello");
答案 0 :(得分:2)
这里最重要的问题是不能在构造函数中调用 start(),因为它违反了Java内存模型并向您展示了未定义的行为。
makeItSo()中的 是 Starter 实例,因此 join()应该使主线程等待直到 run()结束。我没有看到任何竞争机会,但在线程构造函数中调用 start()违反了Java内存模型,因为它暴露了对这两者的不安全引用( Starter )和 x 到另一个帖子。您现在暴露于未定义的行为,这非常糟糕并且难以调试。
有效的陈述顺序直观地是:
int x = 2;
x = 5;
x *= 2;
x = x - 1;
System.out.println(x);
每次打印 9 ,但 NOT 保证,因为Java内存模型已被违反,因此行为最多可能而不是确定。< / p>
请勿从线程的构造函数中调用 start()。有关更多信息,请参阅Brian Goetz关于安全构建技术的developerWorks文章: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html
现在换另一个例子:
Thread t = new Thread(new ThreadDemo());
// this will call run() function
t.start();
// waits for this thread to die
t.join();
System.out.println("Hello");
的行为类似于:
new ThreadDemo().run();
System.out.println("Hello");
运行 t.join()会使在线程 t 上运行的代码表现得像在内联顺序运行一样。 otherThread.join()使调用线程等待 otherThread 完成执行。
重点强调我重复一遍:这里最重要的问题是 start()不能在构造函数中调用,因为它违反了Java内存模型并向你暴露了未定义的行为。
答案 1 :(得分:2)
join
正在做的事情的基本概念是正确的,因为makeItSo
正在调用join
,您会感到困惑,但您似乎并不理解调用/执行方法的上下文。
我已采用您的代码并使用Thread.currentThread().hashCode())
以各种方法打印当前线程的哈希码,例如。
public class Starter extends Thread {
private int x = 2;
public static void main(String[] args) throws Exception {
System.out.println("Main > " + Thread.currentThread().hashCode());
new Starter().makeItSo();
}
public Starter() {
x = 5;
start();
}
public void makeItSo() throws Exception {
System.out.println("makeItSo > " + Thread.currentThread().hashCode());
join(); // ??????
x = x - 1;
System.out.println(x);
}
public void run() {
System.out.println("run > " + Thread.currentThread().hashCode());
x *= 2;
}
}
当我运行代码时,我得到了以下输出
Main > 705927765
makeItSo > 705927765
run > 1870487130
9
正如您所看到的,Main
和makeItSo
都在同一个Thread
中被调用,但run
在另一个Thread
中,这是为什么join
会等到Starter
完成。
可能更容易理解的是使用name
的{{1}}属性,例如......
Thread
哪个输出
public class Starter extends Thread {
private int x = 2;
public static void main(String[] args) throws Exception {
System.out.println("Main > " + Thread.currentThread().getName());
new Starter().makeItSo();
}
public Starter() {
setName("Starter");
x = 5;
start();
}
public void makeItSo() throws Exception {
System.out.println("makeItSo > " + Thread.currentThread().getName());
join(); // ??????
x = x - 1;
System.out.println(x);
}
public void run() {
System.out.println("run > " + Thread.currentThread().getName());
x *= 2;
}
}
请记住,当您在Main > main
makeItSo > main
run > Starter
9
上调用start
时,会生成一个新线程,并且您的代码将继续在当前线程上下文中执行,并且在将来的某个时间,Thread
是调用。
答案 2 :(得分:-1)
主线程等待子线程完成。原因如下: -
调用wait方法的最佳做法是使用线程对象引用。
因此,当主线程调用Starter类的makeItSo()
方法(这是一个线程对象)时,调用join()
会隐式地使主线程等待,直到Starter线程完成。
让我们假设如果你在构造函数中注释start()
调用,则会在线程尚未启动时立即返回,因此它将打印4。
我希望这清楚地说明。