高并发编程-Thread#join方法的使用及使用场景分析

2021-08-17 10:02:30 浏览数 (2)

含义

翻看下源码的注释: Waits for this thread to die

乍理解起来有点晦涩, 简单来说: 等待该线程终止. 需要明确的是主线程等待子线程(假设有个子线程thread)的终止。即在主线程的代码块中,如果碰到了thread.join()方法,此时主线程需要等子线程thread结束了(Waits for this thread to die.),才能继续执行thread.join()之后的代码块

后面会有示例说明


方法及示例

void join()

重点看注释 ,这里就不多说了。

代码语言:javascript复制
package com.artisan.test;

import java.util.stream.IntStream;

public class JoinThreadDemo {

    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName()   " 主线程 start");
        System.out.println("------------------------------------------------");

        // 定义一个线程
        Thread t = new Thread(() -> {
            // 打印 1 到 10 的数字
            IntStream.range(0, 10).forEach(i -> System.out.println(Thread.currentThread().getName()   ">>>>>>"   i));
            Optional.of(Thread.currentThread().getName()   " 打印完毕").ifPresent(System.out::println);

        },"artisan-thread");
        // 启动线程
        t.start();
        // 在主线程中 ,开启了一个新的线程t ,调用 t.join方法,确保该线程执行结束后,才会继续执行主线程中剩下的逻辑
        t.join();

        Optional.of("------------------------------------------------").ifPresent(System.out::println);
        System.out.println(Thread.currentThread().getName()   " 主线程 over");
    }
}

输出:

可以看到 ,调用了 t.join() 后, 主线程main线程,一直等到该线程执行结束后,才继续执行main线程自己剩下的业务逻辑。


我们继续来看下在main线程开启多个线程,分别join的情况

代码语言:javascript复制
package com.artisan.test;

import java.util.Optional;
import java.util.stream.IntStream;

public class JoinThreadDemo {

    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName()   " 主线程 start");
        System.out.println("------------------------------------------------");

        // 定义一个线程
        Thread t = new Thread(() -> {
            // 打印 1 到 10 的数字
            IntStream.range(0, 10).forEach(i -> System.out.println(Thread.currentThread().getName()   ">>>>>>"   i));
            Optional.of(Thread.currentThread().getName()   " 打印完毕").ifPresent(System.out::println);
        },"artisan-thread");

        Thread t2 = new Thread(() -> {
            // 打印 1 到 10 的数字
            IntStream.range(0, 10).forEach(i -> System.out.println(Thread.currentThread().getName()   ">>>>>>"   i));
            Optional.of(Thread.currentThread().getName()   " 打印完毕").ifPresent(System.out::println);
        },"littleART-thread");

        // 启动线程
        t.start();
        t2.start();
        // 在主线程中 ,开启了一个新的线程t ,调用 t.join方法,确保该线程执行结束后,才会继续执行主线程中剩下的逻辑
        t.join();
        t2.join();


        Optional.of("------------------------------------------------").ifPresent(System.out::println);
        System.out.println(Thread.currentThread().getName()   " 主线程 over");
    }
}

日志输出如下

可以看到 , main线程依然是等待t1和t2执行完以后才继续执行自己的逻辑,但是t1 和 t2 是交替执行的,主要取决于CPU的调度,这是正确的。


void join(long millis)

等待多少毫秒后,如果未完成,继续当前线程的任务。

举个例子来演示下

代码语言:javascript复制
package com.artisan.test;

import java.util.Optional;
import java.util.stream.IntStream;

public class JoinThreadDemo {

    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName()   " 主线程 start");
        System.out.println("------------------------------------------------");

        // 定义一个线程
        Thread t = new Thread(() -> {
            try {
                // 先休眠10S,再输出 0 到 9
                Thread.sleep(10_000);
                
                Optional.of(Thread.currentThread().getName()   " 休眠结束...继续干活").ifPresent(System.out::println);

                IntStream.range(0, 10).forEach(i -> System.out.println(Thread.currentThread().getName()   ">>>>>>"   i));
                Optional.of(Thread.currentThread().getName()   " 打印完毕").ifPresent(System.out::println);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"artisan-thread");

        t.start();
        // t 线程等待 1000毫秒 即 1秒,如果未完成,则不等待,继续主线程中逻辑
        t.join(1000);


        Optional.of("---------------等待1秒结束,继续主线程的业务----------------").ifPresent(System.out::println);
        System.out.println(Thread.currentThread().getName()   " 主线程 over");
    }
}

输出:

可以看到 线程t在调用 join(1000),等待1秒后,该任务没有完成,主线程继续执行剩下的逻辑, 执行完以后没有退出 是因为t这个线程是user thread ,而不是daemon thread, 所以还是会继续执行的。


join(long millis, int nanos)

这个其实和 join(long milli)一样 ,只是等待的时间加上纳秒 wait for specified time (milliseconds nanos). 没啥好演示的了。。。


使用场景分析

假设主线程中开启了N个子线程,多个子线程可能有多个任务处理,假设业务要求当主线需要等待子线程处理完以后才能继续剩下的业务逻辑,这个时候如果不是用J.U.C包中的类,用原生的线程中提供的方法来处理的话,就是join了。

0 人点赞