0

下面是我的示例代码,当我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()是在调用方法之前打印的,并且在调用线程“a”的连接方法时必须已经完成了它的执行,此时调用连接必须是无用的。

4

4 回答 4

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");
  }
}
于 2015-07-30T09:29:38.980 回答
2

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

于 2015-07-30T09:32:07.490 回答
0

start()方法调用线程的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.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()可确保您始终在“结束”之前看到“运行”输出,因为 join()等待线程完成。

于 2015-07-30T11:38:23.377 回答
0

我用 join() 尝试了许多场景,“运行”总是在“开始”之后打印,但是当我删除连接语句时,“运行”总是在“开始”之前打印。为什么?

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

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

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

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

如果您想控制事情发生的顺序,那么您必须使用synchronized块、同步对象和方法(例如,.join())。

但要小心!你越是强迫事情以任何特定的顺序发生,你的程序从使用线程中获得的好处就越少。最好设计您的程序,以使线程在大多数情况下可以相互独立地运行。

于 2015-07-30T13:41:00.080 回答