线程如何知道前面有一个连接方法

时间:2015-07-30 09:26:45

标签: java multithreading pthread-join

下面是我的示例代码,当我的a.start()调用它时应创建一个线程并打印"运行"立即。但是为什么在印刷后调用"开始" 20次。

线程" a"决定它不必立即致电run()

public class JoinTest implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(new JoinTest());
        a.start();

        for (int i = 0; i < 20; i++) {
            System.out.print("Begin");
        }
        Thread.sleep(1000);

        a.join();
        System.out.print("\nEnd");
    }

    public void run() {
        System.out.print("\nRun");
    }
}

输出:

BeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBeginBegin
Run
End

我对线程的行为感到困惑。

我认为"run"应该在"begin"之前打印,因为它在调用join()方法之前打印,并且在连接方法时被称为线程&#34; a&# 34;必须完成其执行并且此时调用join必须是无用的。

4 个答案:

答案 0 :(得分:2)

你启动线程,然后立即做一些打印,然后睡觉。看看你的代码,我实际上期望Begin之前看到Run,因为线程是在后台启动的,同时也是你的主要线程继续工作。此外,print方法是同步的,因此您可以在循环中重复获取该锁定,从而减少第二个线程插入的机会。

我已尝试删除Thread.sleep的代码,无论是否有join调用。在这两种情况下,行为都是相同的:Run大部分时间都在最后,有时会设法在Begin个单词之间进行交错。与同步块的简单并发模型完全一样的预期。

您的代码略有不同,可以在其他所有内容之前可靠地打印Run,无论您是否调用join方法。所有改变的都是sleep电话的位置。

public class JoinTest implements Runnable
{
  public static void main(String[] args) throws InterruptedException {
    Thread a = new Thread(new JoinTest());
    a.start();

    Thread.sleep(1000);
    for (int i = 0; i < 20; i++) {
      System.out.print("Begin");
    }

    a.join();
    System.out.print("\nEnd");
  }

  public void run() {
    System.out.print("\nRun");
  }
}

答案 1 :(得分:2)

在线程上调用start()并不一定会立即触发其run()方法的执行。您的线程被标记为已启动,但主线程将其执行追溯到for循环。然后,一旦主线程到达sleep()语句,JVM就会切换到您的线程。

答案 2 :(得分:0)

start()方法调用线程的run()方法,虽然这需要一点时间,这对于您的部分或全部“开始”来说可能已经足够了。循环完成。当我在我的机器上运行时,运行&#39;输出出现在第一个和第二个“开始”之间。尝试使用时间戳输出,这样您就可以看到机器执行每个命令所需的时间:

public class JoinTest implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(new JoinTest());
        a.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("Begin " + System.nanoTime());
        }
        Thread.sleep(1000);

        a.join();
        System.out.println("End " + System.nanoTime());
    }

    public void run() {
        System.out.println("Run " + System.nanoTime());
    }
}

此时拨打a.join()可确保您始终可以看到“运行”状态。在&#39;结束&#39;之前输出,作为join()waits for the thread to complete.

答案 3 :(得分:0)

  

我尝试了很多使用join()的方案,“run”总是在“begin”之后打印但是当我删除join语句时“run”总是在“begin”之前打印。为什么?

因为这是允许的。这就是为什么。一旦调用.start(),就会有两个线程。直到您的程序调用同步方法,如.join(),这完全取决于JVM实现和操作系统,以决定在什么时候运行哪个线程。

来自主线程的a.join()调用强制主线程等待a线程完成其run()方法。

没有a.join()调用,Java语言规范允许 a线程在a.start()在主线程中返回之前完成其工作,并且它允许在main()线程开始运行之前到达a函数末尾的主线程,它允许两者之间发生任何事情。

这完全取决于JVM和操作系统。

如果要控制事件发生的顺序,则必须使用synchronized块,同步对象和方法(例如.join())。

但要注意!您以任何特定顺序强制执行事务的次数越多,程序使用线程所获得的好处就越少。最好设计你的程序,以便线程可以在大多数时间彼此独立运行。