57

我对join()Java 线程中使用的方法感到困惑。在以下代码中:

// Using join() to wait for threads to finish.
class NewThread implements Runnable {

    String name; // name of thread
    Thread t;

    NewThread(String threadname) {
        name = threadname;
        t = new Thread(this, name);
        System.out.println("New thread: " + t);
        t.start(); // Start the thread
    }
// This is the entry point for thread.

    public void run() {
        try {
            for (int i = 5; i > 0; i--) {
                System.out.println(name + ": " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println(name + " interrupted.");
        }
        System.out.println(name + " exiting.");
    }
}

class DemoJoin {

    public static void main(String args[]) {
        NewThread ob1 = new NewThread("One");
        NewThread ob2 = new NewThread("Two");
        NewThread ob3 = new NewThread("Three");
        System.out.println("Thread One is alive: "
                + ob1.t.isAlive());
        System.out.println("Thread Two is alive: "
                + ob2.t.isAlive());
        System.out.println("Thread Three is alive: "
                + ob3.t.isAlive());
// wait for threads to finish
        try {
            System.out.println("Waiting for threads to finish.");
            ob1.t.join();
            ob2.t.join();
            ob3.t.join();
        } catch (InterruptedException e) {
            System.out.println("Main thread Interrupted");
        }
        System.out.println("Thread One is alive: "
                + ob1.t.isAlive());
        System.out.println("Thread Two is alive: "
                + ob2.t.isAlive());
        System.out.println("Thread Three is alive: "
                + ob3.t.isAlive());
        System.out.println("Main thread exiting.");
    }
}

该程序的示例输出如下所示:

New thread: Thread[One,5,main]
New thread: Thread[Two,5,main]
New thread: Thread[Three,5,main]
Thread One is alive: true
Thread Two is alive: true
Thread Three is alive: true
Waiting for threads to finish.
One: 5
Two: 5
Three: 5
One: 4
Two: 4
Three: 4
One: 3
Two: 3
Three: 3
One: 2
Two: 2
Three: 2
One: 1
Two: 1
Three: 1
Two exiting.
Three exiting.
One exiting.
Thread One is alive: false
Thread Two is alive: false
Thread Three is alive: false
Main thread Exiting

在上面的代码中:

  1. 我无法理解程序的执行流程,并且在ob1创建时调用构造函数, t.start()但仍然run()没有执行方法,而是main()方法继续执行。那么为什么会这样呢?

  2. join()方法用于等到调用它的线程没有终止,但是在输出中我们看到线程的备用输出为什么?

如果使用joinis this 那么使用synchronized??

我知道我在这里遗漏了一个基本概念,但我无法弄清楚,所以请帮忙。

4

12 回答 12

166

你必须明白,线程调度是由线程调度器控制的。所以,在正常情况下,你不能保证线程的执行顺序。

但是,您可以使用join()来等待线程完成其工作。

例如,在您的情况下

ob1.t.join();

t在线程完成运行之前,该语句不会返回。

尝试这个,

class Demo {
   Thread t = new Thread(
                 new Runnable() {
                     public void run () {
                         //do something
                     }
                  }
    );
    Thread t1 = new Thread(
                 new Runnable() {
                     public void run () {
                         //do something
                     }
                  }
    );
    t.start(); // Line 15
    t.join();  // Line 16
    t1.start();
}

在上面的示例中,您的主线程正在执行。当它遇到第 15 行时,线程 t 在线程调度程序中可用。一旦主线程到达第 16 行,它将等待线程t完成。

注意t.join没有对 threadt或 thread做任何事情t1。它只影响调用它的线程(即main()线程)。

编辑:

t.join();需要在try块内,因为它throwsInterruptedException异常,否则在编译时会出错。所以,它应该是:

try{
    t.join();
}catch(InterruptedException e){
    // ...
}
于 2013-08-28T05:51:27.513 回答
6

首先,当您创建时,ob1将调用构造函数并开始执行。当时t.start()也在单独的线程中运行。请记住,当创建一个新线程时,它会与主线程并行运行。这就是为什么 main 用下一条语句再次开始执行。

andJoin()语句用于防止子线程成为孤儿。意味着如果您没有在主类中调用join(),则主线程将在其执行后退出,并且子线程仍将在那里执行语句。Join()将等到所有子线程完成执行,然后只有 main 方法将退出。

看完这篇文章,很有帮助。

于 2013-08-28T05:13:12.833 回答
4

我无法理解程序的执行流程,并且当创建 ob1 时,会调用构造函数,其中 t.start() 被写入但仍然没有执行 run() 方法,而是 main() 方法继续执行。那么为什么会这样呢?

这取决于线程调度程序,因为 main 共享相同的优先级顺序。调用 start() 并不意味着 run() 将立即被调用,它取决于线程调度程序何时选择运行您的线程。

join() 方法用于等到调用它的线程没有终止,但是在输出中我们看到线程的备用输出为什么?

这是因为代码中的 Thread.sleep(1000) 。删除该行,您将看到 ob1 在 ob2 之前完成,而 ob2 又在 ob3 之前完成(正如 join() 所预期的那样)。话虽如此,这一切都取决于 ob1 ob2 和 ob3 何时开始。调用 sleep 将暂停线程执行 >= 1 秒(在您的代码中),让调度程序有机会调用其他线程等待(相同优先级)。

于 2014-02-25T06:23:45.687 回答
1

线程的第一条规则 - “线程很有趣”......

我无法理解程序的执行流程,当创建 ob1 时,构造函数被调用,t.start()但仍然run()没有执行方法,而是main()方法继续执行。那么为什么会这样呢?

这正是应该发生的事情。当您调用Thread#start时,会创建线程并安排执行,它可能会立即发生(或足够接近它),也可能不会。它归结为线程调度程序。

这归结为如何安排线程执行以及系统中发生了什么。通常情况下,每个线程都会被赋予一小段时间来执行,然后再将其放回“睡眠”状态并允许另一个线程执行(显然在多处理器环境中,一次可以运行多个线程,但让我们尝试一下并保持简单;))

线程也可以yield执行,让系统中的其他线程有机会执行。

你可以试试

NewThread(String threadname) {
    name = threadname;
    t = new Thread(this, name);
    System.out.println("New thread: " + t);
    t.start(); // Start the thread
    // Yield here
    Thread.yield();
}

它可能会对线程的运行方式产生影响......同样,你可以sleep在一小段时间内,但这可能会导致你的线程在一段时间内被忽略执行(有时你想要这个,有时你不)...

join()方法用于等到调用它的线程没有终止,但是在输出中我们看到线程的备用输出为什么?

您提出问题的方式是错误的……join将等待Thread它被召唤死亡后再返回。例如,如果您依赖 a 的结果,则Thread可以在尝试检索结果之前join知道 a 何时结束。Thread

同样,您可以轮询线程,但这会占用 CPU 周期,而这些周期可以更好地被Thread...

于 2013-08-28T05:14:43.020 回答
1

JVM 和底层操作系统在调度事物时有相当大的自由度。在您看到单个线程的输出之前,您一直到“等待线程完成”这一事实可能仅仅意味着线程启动需要更长的时间(即在线程变为“的那一刻之间需要一些时间”活着”,并且当run()方法实际开始执行时)。可以想象,您可以更快地看到线程输出,但无论哪种方式都不能保证。

至于join(),它只保证它之后的任何事情都只会在你加入的线程完成后发生。因此,当您连续进行三个join()调用时,并不意味着线程应该以特定顺序结束。它只是意味着您将首先等待ob1。一旦ob1完成,ob2ob3可能仍在运行,或者它们可能已经完成。如果它们完成,您的其他join()调用将立即返回。

当多个线程访问同一个对象并对其进行更改时,专门使用同步。同步块保证永远不会被两个线程同时执行——即执行它的线程将同步对象全部归于自己。

于 2013-08-28T05:22:15.250 回答
1

当创建 ob1 时,将调用构造函数,其中写入“t.start()”,但仍不执行 run() 方法,而是进一步执行 main() 方法。那么为什么会这样呢?

这里你的线程和主线程具有相同的优先级。相同优先级线程的执行完全取决于。Thread schedular你不能指望先执行哪个。

join() 方法用于等到调用它的线程没有终止,但是在输出中我们看到线程的备用输出为什么?

在这里,您从主线程调用以下语句。

     ob1.t.join();
     ob2.t.join();
     ob3.t.join();

所以主线程等待ob1.t, ob2.t,ob3.t线程死亡(查看Thread#join doc)。所以所有三个线程都成功执行,之后主线程完成

于 2013-08-28T05:22:54.050 回答
1

我的评论:

当我看到输出时,输出与线程名称一、二、三混合在一起,它们同时运行。我不确定你什么时候说线程没有通过 main 方法运行。

不知道我是否理解你的问题。但我的回答是我能理解的,希望它可以帮助你。

1)然后您创建了对象,它调用了构造函数,在构造中它有 start 方法,该方法启动线程并执行 run() 方法中写入的内容。

因此,当您创建 3 个对象(3 个线程 - 1、2、3)时,所有 3 个线程同时开始执行。

2)加入和同步它们是两件不同的事情,同步是当有多个线程共享一个公共资源并且一个线程应该一次使用该资源时。例如,DepositThread、WithdrawThread 等线程共享一个公共对象作为 BankObject。因此,当 DepositThread 正在运行时,如果它们同步,WithdrawThread 将等待。wait()、notify()、notifyAll() 用于线程间通信。请谷歌了解更多。

关于Join(),它是当多个线程正在运行时,但是你加入了。例如,如果有两个线程 t1 和 t2 并且它们在多线程环境中运行,则输出将是: t1-0 t2-0 t1-1 t2-1 t1-2 t2-2

我们使用 t1.join(),它会是:t1-0 t1-1 t1-2 t2-0 t2-1 t2-2

当有时您在某些情况下不混淆线程并且一个依赖另一个要完成的线程(不在共享资源中)时,这是实时使用的,因此您可以调用 join() 方法。

于 2013-08-28T05:35:25.967 回答
1

没有文字只是运行代码

// Thread class
public class MyThread extends Thread {

    String result = null;

    public MyThread(String name) {
        super(name);
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {

            System.out.println("Hello from " + this.getName());
        }
        result = "Bye from " + this.getName();
    }
}

主班

public class JoinRND {
    public static void main(String[] args) {

        System.out.println("Show time");
        // Creating threads
        MyThread m1 = new MyThread("Thread M1");
        MyThread m2 = new MyThread("Thread M2");
        MyThread m3 = new MyThread("Thread M3");

        // Starting out Threads
        m1.start();
        m2.start();
        m3.start();
        // Just checking current value of thread class variable
        System.out.println("M1 before: " + m1.result);
        System.out.println("M2 before: " + m2.result);
        System.out.println("M3 before: " + m3.result);
        // After starting all threads main is performing its own logic in
        // parallel to other threads
        for (int i = 0; i < 1000; i++) {

            System.out.println("Hello from Main");
        }

        try {

            System.out
                    .println("Main is waiting for other threads to get there task completed");
            m1.join();
            m2.join();
            m3.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("M1 after" + m1.result);
        System.out.println("M2 after" + m2.result);
        System.out.println("M3 after" + m3.result);

        System.out.println("Show over");
    }
}
于 2017-04-26T08:01:19.563 回答
1

线程调度器负责线程的调度。所以每次运行程序,都无法保证线程的执行顺序。假设您有一个名为 threadOne 的线程对象,并且如果在 threadOne 上调用 join(),如下所示:

threadOne.join()

然后所有当前正在执行的线程将暂停,直到 thread1 完成执行或终止。

考虑以下代码:

class RunnableSample implements Runnable {
    private Thread t;
    private String threadName;

    public RunnableSample(String name) {
        this.threadName = name;
    }
    public void run() {
        try {
            for(int i = 4; i >= 1; i--) {
                System.out.println(Thread.currentThread().getName() + ", " + i);
            Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            System.out.println(threadName + " interrupted");
        }
    }
    public void start() {
        if(t == null)
            t = new Thread(this, threadName);
        t.start();
        try {
            t.join();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
public class RunnableDemo {
    public static void main(String[] args) {
        RunnableSample r1 = new RunnableSample("threadOne");
        r1.start();

        RunnableSample r2 = new RunnableSample("threadTwo");
        r2.start();

        RunnableSample r3 = new RunnableSample("threadThree");
        r3.start();
     }
}

上述程序的输出将是:

threadOne, 4
threadOne, 3
threadOne, 2
threadOne, 1
threadTwo, 4
threadTwo, 3
threadTwo, 2
threadTwo, 1
threadThree, 4
threadThree, 3
threadThree, 2
threadThree, 1

由于首先在 threadOne 上调用 join(),因此 threadTwo 和 threadThree 将暂停,直到 threadOne 终止。(注意 threadOne、threadTwo 和 ThreadThree 都已启动)。现在线程正在按特定顺序执行。如果在我们的示例中没有在线程上调用 join(),那么将没有线程的执行顺序。

public void start() {
    if(t == null)
        t = new Thread(this, threadName);
    t.start();
}

它的输出将是:

threadOne, 4
threadThree, 4
threadTwo, 4
threadTwo, 3
threadThree, 3
threadOne, 3
threadOne, 2
threadThree, 2
threadTwo, 2
threadOne, 1
threadThree, 1
threadTwo, 1

来到同步,如果你想控制多个线程对任何共享资源的访问,这很有用。如果您只想限制一个线程访问共享资源,那么同步是最好的方法。

于 2017-06-15T04:46:13.317 回答
1

我在了解比赛条件时遇到了 join() ,我将消除我的疑虑。所以让我们举这个小例子

Thread t2 = new Thread(
             new Runnable() {
                 public void run () {
                     //do something
                 }
              }
);
Thread t1 = new Thread(
             new Runnable() {
                 public void run () {
                     //do something
                 }
              }
);
t2.start(); //Line 11
t1.start(); //Line 12
t2.join();  //Line 13
t1.join();  //Line 14
System.out.print("<Want to print something that was being modified by t2 and t1>")

我的 AIM
三个线程正在运行,即 t1、t2 和主线程。我想在 t1 和 t2 完成后打印一些东西。打印操作在我的主线程上,因此对于预期的答案,我需要让 t1 和 t2 完成,然后打印我的输出。

所以 t1.join() 只是让主线程等待,直到 t1 线程完成,然后再进入程序的下一行。

这是GeeksforGeeks的定义:

java.lang.Thread 类提供了 join() 方法,该方法允许一个线程等待另一个线程完成其执行。

这里有一个问题可以解决你的疑惑

Q->当程序在第 13 行处理 t2.join() 时,t1 线程是否会获得由线程调度程序运行的时间片?

ANS->是的,它将有资格获得运行时间片,因为我们已经通过在第 11行运行 t1.start() 行使其符合条件。
t2.join() 仅在 JVM 将转到下一行时应用条件,即第 14 行。
也有可能 t1 可能在第 13 行完成处理。

于 2018-09-17T13:31:24.560 回答
0

join() 是 java.lang.Thread 类的一个实例方法,我们可以使用 join() 方法来确保从 main 开始的所有线程必须按照它们开始的顺序结束,并且 main 应该最后结束。换句话说,等待这个线程死亡。

异常: join() 方法抛出 InterruptedException。

线程状态: 当在线程上调用 join() 方法时,它会从运行状态进入等待状态。并等待线程死亡。

同步块: 线程在调用join()方法之前不需要获取对象锁,即可以从同步块外部调用join()方法。

等待时间: join():等待这个线程死掉。

public final void join() throws InterruptedException;

此方法在内部调用 join(0)。而 timeout 为 0 意味着永远等待;

join(long millis) – 同步方法 最多等待毫秒毫秒,让该线程终止。超时 0 意味着永远等待。

public final synchronized void join(long millis)
    throws InterruptedException;

public final synchronized void join(long millis, int nanos)
    throws InterruptedException;

连接方法示例

class MyThread implements Runnable {
     public void run() {
           String threadName = Thread.currentThread().getName();
           Printer.print("run() method of "+threadName);
           for(int i=0;i<4;i++){
                Printer.print("i="+i+" ,Thread="+threadName);
           }         
     }
}

public class TestJoin {
     public static void main(String...args) throws InterruptedException {
           Printer.print("start main()...");

           MyThread runnable = new MyThread();
           Thread thread1=new Thread(runnable);
           Thread thread2=new Thread(runnable);

           thread1.start();
           thread1.join();

           thread2.start();
           thread2.join();

           Printer.print("end main()");
     }
}

class Printer {
     public static void print(String str) {
           System.out.println(str);
     }
}

Output:
     start main()...
     run() method of Thread-0
     i=0 ,Thread=Thread-0
     i=1 ,Thread=Thread-0
     i=2 ,Thread=Thread-0
     i=3 ,Thread=Thread-0
     run() method of Thread-1
     i=0 ,Thread=Thread-1
     i=1 ,Thread=Thread-1
     i=2 ,Thread=Thread-1
     i=3 ,Thread=Thread-1
     end main()

注意:调用 thread1.join() 使主线程等待直到 Thread-1 死亡。

让我们检查一个程序来使用 join(long millis)

首先,将在 Thread-1 上调用 join(1000),但是一旦 1000 毫秒,主线程可以恢复并启动 thread2(主线程不会等待 Thread-1 死亡)。

class MyThread implements Runnable {
     public void run() {
           String threadName = Thread.currentThread().getName();
           Printer.print("run() method of "+threadName);
           for(int i=0;i<4;i++){
                try {
                     Thread.sleep(500);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
                Printer.print("i="+i+" ,Thread="+threadName);
           }         
     }
}

public class TestJoin {
     public static void main(String...args) throws InterruptedException {
           Printer.print("start main()...");

           MyThread runnable = new MyThread();
           Thread thread1=new Thread(runnable);
           Thread thread2=new Thread(runnable);

           thread1.start();

           // once 1000 millisec are up,
           // main thread can resume and start thread2.
           thread1.join(1000);

           thread2.start();
           thread2.join();

           Printer.print("end main()");
     }
}

class Printer {
     public static void print(String str) {
           System.out.println(str);
     }
}

Output:
     start main()...
     run() method of Thread-0
     i=0 ,Thread=Thread-0
     run() method of Thread-1
     i=1 ,Thread=Thread-0
     i=2 ,Thread=Thread-0
     i=0 ,Thread=Thread-1
     i=1 ,Thread=Thread-1
     i=3 ,Thread=Thread-0
     i=2 ,Thread=Thread-1
     i=3 ,Thread=Thread-1
     end main()

更多信息见我的博客:

http://javaexplorer03.blogspot.in/2016/05/join-method-in-java.html

于 2016-05-12T04:06:05.957 回答
0

看概念很简单。

1) 所有线程都在构造函数中启动,因此处于准备运行状态。Main 已经是正在运行的线程。

2) 现在你调用了 t1.join()。这里发生的是主线程在 t1 线程后面打结。所以你可以想象一个更长的线程,主线程连接到 t1 的下端。

3) 现在可以运行三个线程:t2、t3 和组合线程(t1 + main)。

4)现在因为直到 t1 完成主要不能运行。所以其他两个连接语句的执行已经停止。

5) 所以调度程序现在决定运行上面提到的(在第 3 点中)哪个线程来解释输出。

于 2018-12-10T21:48:44.683 回答