Java线程问题,输出一致

Java线程问题,输出一致,java,multithreading,Java,Multithreading,在下面的代码中,答案总是从0 1 2 3完成开始。我只是想知道这怎么可能 如果有人能帮助实现输出的一致性,那就太好了 公共类TestOne扩展线程{ /** *@param args */ 公共静态void main(字符串[]args)引发异常{ //TODO自动生成的方法存根 线程t=新线程(newtestone()); t、 start(); System.out.println(“已启动”); t、 join(); 系统输出打印项次(“完成”); } 公开募捐{ 对于(int i=0;i

在下面的代码中,答案总是从0 1 2 3完成开始。我只是想知道这怎么可能

如果有人能帮助实现输出的一致性,那就太好了
公共类TestOne扩展线程{
/**
*@param args
*/
公共静态void main(字符串[]args)引发异常{
//TODO自动生成的方法存根
线程t=新线程(newtestone());
t、 start();
System.out.println(“已启动”);
t、 join();
系统输出打印项次(“完成”);
}
公开募捐{

对于(int i=0;i最有可能得到相同的结果,因为在大多数情况下,主线程启动一个新线程,然后在该新线程有机会打印任何内容之前,主线程打印其
started
消息。主线程中的
join
会强制它等待另一个线程完成,然后它会执行以下操作:ints
完成

这里有一个竞态条件。在启动第二个线程的那一刻,它不确定输出行的顺序(除了前面提到的通过
wait
调用而确定的
complete
行)

但是,比赛条件并不能保证你在多次比赛中获得不同的结果,只能保证这是可能的。当然,你仍然不应该依赖于这种行为

例如,以下代码:

public class TestOne extends Thread {
    public static void main (String[] args) throws Exception {
        Thread t = new Thread (new TestOne());
        t.start();

        Thread.sleep (1000);  // <- Added this.

        System.out.println ("Started");
        t.join();
        System.out.println ("Complete");
    }

    public void run() {
        for (int i = 0; i < 4; i++) {
            System.out.println (i);
        }
    }
}
尽管如此,由于线程“预热”可能需要超过一秒钟的时间,所以顺序也不能保证,-
睡眠很少是解决争用条件的好办法。我在这里只是为了说明原因而使用它。

t.join()
表示“阻塞直到线程
t
完成”,这解释了“完成”的原因最后一名

编辑:在回答问题时重新“开始”

调用Thread.start()意味着“请安排此线程运行”,它将在java想要启动它时启动。在t.start()和println()之间发生这种情况的可能性取决于平台,但在我使用过的系统上很小(thx到@Stephen C for platform info

然而,该代码输出0、开始、1、2、3、完成:

    Thread t = new Thread(new TestOne());
    t.start();
    try
    {
        Thread.sleep(100); // Added sleep between t.start() and println()
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
    System.out.println("Started");
    t.join();
    System.out.println("Complete");

当你说
Thread t=new Thread();
没有什么特别的事情发生,因为你不是在创建一个
“Thread”
本身。它只是一个具有“特征”的对象,可以成为执行线程。要使该对象成为
“Thread”
你必须调用
t.start()
这就是魔力所在

当你说
t.start()时
JVM为新创建的线程创建一个新堆栈以运行。它将该堆栈与相关的线程对象相关联。然后使其可用于调度。基本上,这意味着它将线程排队到JVM调度程序中,并且在下一个时间段中也可用于执行。JVM实际上做的远不止这些,对于你的例子,我的回答过于简单了

在创建所有这些线程+堆栈的同时,您的主线程总是有机会移动到下一条指令,在您的情况下,这条指令是
System.out.println(“started”);

从理论上讲,您所说的是正确的,
“Started”
可以在
0、1、2、3之间的任何地方。然而,实际上,由于
t.start()
是一种“昂贵”的方法,因此需要一些时间才能完成,在此期间,主线程通常有机会执行其下一条指令


如果您想了解
t.start()的详细信息查看Java源代码。

简单地说,启动线程相对于主线程的调度依赖于JVM实现。但是,尽管如此,大多数实现(如果不是全部的话)都会让启动线程一直运行到完成其时间片,直到它阻塞某个东西,或者直到它被JVM抢占更高优先级的线程(如果使用抢占式调度,也取决于JVM实现)


Java规范只是没有说太多关于线程的非常具体的内容,故意为JVM编写器提供最大的实现范围。

显然,您看到了一致的结果(以及这个特定的结果)因为在您的机器上,对子线程的
run
方法的调用始终发生在主线程中的
println
之后

为什么它是一致的

很简单,因为您的平台的本机线程库以一致的方式运行

典型的现代Java虚拟机实现使用主机操作系统的本机线程支持来实现Java线程,并执行Java线程调度。在您的计算机上,本机线程实现似乎始终允许当前线程从
thread.start()返回
立即调用并继续执行

但是,不能保证这种情况总是会发生。例如,如果机器负载很重,而主线程刚刚耗尽了其当前时间片,则它可能会在
start
调用期间或之后立即被取消调度,从而允许新线程先运行

此外,在另一个平台上,正常的调度程序行为可能会有所不同。调度程序可以始终如一地导致当前线程重新调度,并让新线程先走。或者它可能“随机”发生

JVM和Java库规范故意不指定哪个线程“先走”,以允许线程实现中的差异……以及由于硬件、系统负载等方面的差异而产生的变化



底线-您看到的明显的一致性是一个“工件”,您不应该依赖它,特别是如果您希望应用程序在各种JVM上工作。

当您启动线程时,它需要时间(没有任何东西是免费的,或者是在
0
1
2
3
Started
Complete
    Thread t = new Thread(new TestOne());
    t.start();
    try
    {
        Thread.sleep(100); // Added sleep between t.start() and println()
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
    System.out.println("Started");
    t.join();
    System.out.println("Complete");