关于java中的join方法

时间:2015-11-13 02:59:33

标签: java multithreading

在下面的代码中,主线程将调用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");

3 个答案:

答案 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

正如您所看到的,MainmakeItSo都在同一个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。 我希望这清楚地说明。