Java多线程概念和join()方法

时间:2013-08-28 05:00:59

标签: java multithreading synchronized pthread-join

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

// 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()方法用于等待它被调用的线程没有终止,但是在输出中我们看到线程的备用输出为什么?

  3. 如果使用join,那么synchronized的用途是什么?

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

12 个答案:

答案 0 :(得分:158)

您必须了解,线程调度由线程调度程序控制。因此,您无法保证正常情况下线程的执行顺序。

但是,您可以使用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没有对线程t或线程t1做任何事情。它只影响调用它的线程(即main()线程)。

<强>编辑:

t.join();需要位于try块内,因为它throws InterruptedException异常,否则您将在编译时遇到错误。所以,它应该是:

try{
    t.join();
}catch(InterruptedException e){
    // ...
}

答案 1 :(得分:5)

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

Join()语句用来防止子线程成为孤儿。表示如果你没有在主类中调用join(),那么主线程就会退出执行后,子线程仍然会执行语句。 Join()将等待所有子线程完成其执行,然后只有main方法将退出。

浏览this文章,帮助很多。

答案 2 :(得分:3)

  

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

这取决于线程调度程序作为主要共享相同的优先级顺序。调用start()并不意味着会立即调用run(),它在选择运行线程时依赖于线程调度程序。

  

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

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

答案 3 :(得分: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在返回之前死亡。例如,如果您依赖Thread的结果,则可以使用join知道Thread何时结束,然后再尝试检索结果。

同样,您可以轮询该线程,但这将占用Thread可以更好地使用的CPU周期......

答案 4 :(得分:1)

JVM和底层操作系统在安排事务时具有相当大的自由度。在看到各个线程的输出之前,你已经完成了“等待线程完成”这一事实,这可能只是意味着线程启动需要更长的时间(即线程变为“需要一段时间”) “并且当 run()方法实际开始执行时”。你可以想象更快地看到线程输出,但无论如何都无法保证。

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

当多个线程访问同一个对象并对其进行更改时,将专门使用

synchronized 。保证同步块永远不会被两个线程同时执行 - 即执行它的线程将同步对象全部置于其自身。

答案 5 :(得分:1)

  

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

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

  

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

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

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

所以主线程等待ob1.tob2.tob3.t线程死掉(查看Thread#join doc)。所以三个线程都成功执行,主线程完成后

答案 6 :(得分:1)

我的评论:

当我看到输出时,输出混合了一个,两个,三个作为线程名称并且它们同时运行。我不确定你说什么时候主线程没有运行线程。

不确定我是否理解你的问题。但我正在回答我能理解的问题,希望它可以帮到你。

1)然后你创建了对象,它称为构造函数,在构造中它有start方法,它启动线程并执行run()方法内部写的内容。

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

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

关于Join(),它是在多个线程运行时,但是你加入了。例如如果有两个线程t1和t2,并且在多线程env中它们运行,输出将是: 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()方法。

答案 7 :(得分: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");
    }
}

答案 8 :(得分: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

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

答案 9 :(得分: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-> ,当程序在 Line处理t2.join()时,t1线程将获取线程调度程序要运行的时间片   13

     

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

答案 10 :(得分:0)

join()是java.lang.Thread类的实例方法,我们可以使用join()方法确保从main开始的所有线程必须以它们开始的顺序结束,而main应该以last结尾。换句话说,等待这个线程死掉。

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

主题状态: 当在线程上调用join()方法时,它会从运行状态变为等待状态。等待线程死亡。

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

等待时间 加入(): 等待这个线程死亡。

public final void join() throws InterruptedException;

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

加入(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

答案 11 :(得分:0)

看到这个概念很简单。

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

2)现在,您调用了t1.join()。此处发生的是主线程在t1线程之后被打结。因此,您可以想象一个更长的线程,其中主线程连接到t1的下端。

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

4)现在直到t1完成为止,主电源无法运行。因此其他两个join语句的执行已停止。

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