一塊兒學併發編程 - 線程Join分析

因爲前段時間比較忙,線程這快學習停滯了,只能利用週日的時間來寫寫博客了,多線程Join方法的做用就是把指定的線程加入到當前線程,讓主線程等待子線程結束以後才能繼續運行,從而完成同步操做java

<!-- more -->git

介紹

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毫秒以上ide

採集完成,消耗 1
M1開始採集
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;
        }
    }
}

說明this

從代碼中,咱們能夠發現。當millis==0時,會進入while(isAlive())循環,系統會判斷主線程是否處於活躍狀態,若是處於活躍狀態,主線程就會不停的等待。spa

問題.net

爲何子線程調用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(歡迎調戲)

相關文章
相關標籤/搜索