一起学并发编程

来源:互联网 发布:linux vi编辑器 查找 编辑:程序博客网 时间:2024/05/01 15:38

由于前段时间比较忙,线程这快学习停滞了,只能利用周日的时间来写写博客了,多线程Join方法的作用就是把指定的线程加入到当前线程,让主线程等待子线程结束之后才能继续运行,从而完成同步操作

介绍

join() 的作用:让主线程等待子线程结束之后才能继续运行,首先先来看下以采集为案例的代码,统计采集所消耗的时长

需求:当所有线程任务执行完毕,统计最终消耗时长

public class ThreadJoin {    public static void main(String[] args) throws InterruptedException {        long startTime = System.currentTimeMillis();        Thread t1 = new Thread(new CaptureRunnable("M1", 5_000L));        Thread t2 = new Thread(new CaptureRunnable("M2", 3_000L));        Thread t3 = new Thread(new CaptureRunnable("M3", 2_000L));        t1.start();        t2.start();        t3.start();        System.out.println("采集完成,消耗 " + (System.currentTimeMillis() - startTime));    }}class CaptureRunnable implements Runnable {    private String machineName;//采集任务名    private Long spendTime;//采集工作消耗时长    public CaptureRunnable(String machineName, Long spendTime) {        this.machineName = machineName;        this.spendTime = spendTime;    }    @Override    public void run() {        try {            System.out.println(machineName + "开始采集");            Thread.sleep(spendTime);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

ThreadJoin的代码中,我们创建了主线程main,和实现了RunnableCaptureRunnable子线程,运行main方法,可以看到在未使用join()的情况下,统计结果并不理想,正确输出应该是5000毫秒以上

采集完成,消耗 1M1开始采集M2开始采集M3开始采集

使用join

start方法下添加join操作,运行main方法,发现不管那个线程先执行,结果都是5000毫秒以上,因为主线程main接收到了M1,M2,M3三个线程的join指令,这个时候主线程则会处于阻塞状态,直到子线程执行完毕后才会继续执行下去…

t1.start();t2.start();t3.start();t1.join();t2.join();t3.join();
M2开始采集M1开始采集M3开始采集采集完成,消耗 5001

部分join

去掉M1线程调用的join,然后运行main方法,从日志输出中可以发现,main会等待M2,M3执行完毕后才会继续执行下去

M1开始采集M3开始采集M2开始采集采集完成,消耗 3001

源码分析

public final void join() throws InterruptedException {    join(0);}public final synchronized void join(long millis)throws InterruptedException {    long base = System.currentTimeMillis();    long now = 0;    if (millis < 0) {        throw new IllegalArgumentException("timeout value is negative");    }    if (millis == 0) {        while (isAlive()) {            wait(0);        }    } else {        while (isAlive()) {            long delay = millis - now;            if (delay <= 0) {                break;            }            wait(delay);            now = System.currentTimeMillis() - base;        }    }}

说明

从代码中,我们可以发现。当millis==0时,会进入while(isAlive())循环,系统会判断主线程是否处于活跃状态,如果处于活跃状态,主线程就会不停的等待。

问题

为什么子线程调用join()阻塞的却是主线程呢?join()方法中的isAlive()应该是判断子线程是否处于活跃的状态,对应的wait(0)也应该是让子线程等待才对

答案

首先从源码中我们可以发现它是被synchronized修饰的方法,当前线程的对象调用join后,其实获取到了子线程M1,M2,M3的锁,当子线程锁释放后才会继续执行主线程的操作

jvisualvm分析

使用jvisualvm分析器,可以发现Thread-1-3与主线程main,处于等待的是主线程,子线程因为调用了sleep处于休眠状态(为了演示耗时操作)

- 说点什么

全文代码:https://git.oschina.net/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter3

  • 个人QQ:1837307557
  • battcn开源群(适合新手):391619659

微信公众号:battcn(欢迎调戏)

原创粉丝点击