Thread的API详细介绍(三)线程的join

1 join方法介绍

当前线程等待子线程死亡,才会执行。与sleep一样它也是一个可中断的方法,也就是说,如果有其他线程执行了对当前线程的interrupt操作,它也会捕获到中断信号并且擦除线程的interrupt标识,Thread的API为我们提供了三个不同的join方法,具体如下:

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

什么叫当前线程等待子线程死亡,才会执行?运行下面的代码

public static void main(String[] args) throws InterruptedException {
   // 创建一个线程,打印1 到1000
   Thread thread = new Thread(() -> {
       try {
           IntStream.range(1, 1000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
       } catch (Exception e) {
           e.printStackTrace();
       }
   },"MyThread");
   thread.start();
   // thread.join();
   // 主线程进行同样的操作
   IntStream.range(1, 1000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
}

会发现输出是交替输出的:

main ==> 252
main ==> 253
main ==> 254
main ==> 255
MyThread ==> 98
MyThread ==> 99
MyThread ==> 100
MyThread ==> 101
MyThread ==> 102
MyThread ==> 103
MyThread ==> 104
main ==> 256
main ==> 257
main ==> 258

打开注释掉的代码:

thread.join();

发现不是就不是交替输出,而是MyThread先执行完。

MyThread ==> 997
MyThread ==> 998
MyThread ==> 999
main ==> 1
main ==> 2
main ==> 3
main ==> 4
main ==> 5

join方法会使当前线程永远地等待下去,直到期间被另外的线程中断,或者join的线程执行结束,当然你也可以使用join的另外两个重载方法,指定毫秒数,在指定的时间到达之后,当前线程也会退出阻塞

注意:是等待子线程结束,比如这里是在主线程中创建的MyThead线程,所以主线程会等待MyThead执行结束。

在主线程中创建两个这样的线程,并且调用join

    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程,打印1 到1000
        Thread thread1 = new Thread(() -> {
            try {
                IntStream.range(1, 10000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"MyThread1");

        Thread thread2 = new Thread(() -> {
            try {
                IntStream.range(1, 10000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"MyThread2");
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        // 主线程进行同样的操作
        IntStream.range(1, 3).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
    }
  • 主线程的开辟的这两个子线程还是交替随机打印
MyThread1 ==> 3227
MyThread1 ==> 3228
MyThread1 ==> 3229
MyThread1 ==> 3230
MyThread1 ==> 3231
MyThread2 ==> 3042
MyThread2 ==> 3043
MyThread2 ==> 3044
MyThread2 ==> 3045
MyThread2 ==> 3046
  • 主线程会等到这两个子线程执行结束,才会执行自己的打印任务

2 join的简单应用示例

我们需要采集三个机器的数据,采集完三个机器的数据之后,统一进行保存准备使用三个线程分别对三个机器进行采集

该例子是典型的串行任务局部并行化处理。

  1. 根据要采集的数据定义一个数据模型,这里就简单声明
public class Data {
    private String data;

    public Data(String data) {
        this.data = data;
    }

    public String getData() {
        return data;
    }

    @Override
    public String toString() {
        return "Data{" +
                "data='" + data + '\'' +
                '}';
    }
}

  1. 定义一个接口CaptureData,定义采集数据的规范
public interface CaptureData {

    Data captureData();
}

  1. 定义一个采集任务,实现Runnable和CaptureData接口
// 定义采集的任务
@Slf4j
public class CaptureDataTask implements Runnable, CaptureData {
    // 机器的名字
    private final String machineName;
    // 这次任务花费的时间,每个机器采集的时间可能不同,
    private Long spendTime;
    // 采集的结果
    private Data result;

    // 这里模拟每个采集的时间不同,在构造中也加入spendTime
    public CaptureDataTask(String machineName, Long spendTime) {
        this.machineName = machineName;
        this.spendTime = spendTime;
    }

    @Override
    public void run() {
        try {
            // 模拟采集数据的过程
            log.info("[{}]{}开始采集数据", System.currentTimeMillis(), machineName);
            result = captureData();
            Thread.sleep(spendTime);
            log.info("[{}]{}采集数据结束", System.currentTimeMillis(), machineName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 返回采集的结果
    public Data getResult() {
        return result;
    }

    @Override
    public Data captureData() {
        // 模拟
        return new Data(machineName + "的数据");
    }
}

tips: 不管是Thread的run方法,还是Runnable接口,都是void返回类型,如果你想通过某个线程的运行得到结果,就需要自己定义一个返回的接口。

  1. 没有使用join
public static void main(String[] args) {
    long startTimeStamp = System.currentTimeMillis();
    Thread thread1 = new Thread(new CaptureDataTask("机器1", 10000L));
    Thread thread2 = new Thread(new CaptureDataTask("机器2", 20000L));
    Thread thread3 = new Thread(new CaptureDataTask("机器3", 30000L));

    thread1.start();
    thread2.start();
    thread3.start();
    long endTimeStamp = System.currentTimeMillis();
    log.info("save data begin at [{}], end at [{}]", startTimeStamp, endTimeStamp);
}

  1. 测试:使用join
@Slf4j
public static void main(String[] args) throws InterruptedException {
        CaptureDataTask task1 = new CaptureDataTask("机器1", 10000L);
        Thread thread1 = new Thread(task1);
        CaptureDataTask task2 = new CaptureDataTask("机器2", 20000L);
        Thread thread2 = new Thread(task2);
        CaptureDataTask task3 = new CaptureDataTask("机器3", 30000L);
        Thread thread3 = new Thread(task3);

        thread1.start();
        thread2.start();
        thread3.start();
        thread1.join();
        thread2.join();
        thread3.join();
        Data result1 = task1.getResult();
        Data result2 = task2.getResult();
        Data result3 = task3.getResult();
        log.info("开始保存数据:[{}],[{}],[{}]", result1, result2, result3);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值