JAVA線程池原理源碼解析—爲何啓動一個線程池,提交一個任務後,Main方法不會退出?

public static void main(String[] args) {

        ExecutorService service = Executors.newFixedThreadPool(10);

        service.submit(() -> System.out.println("Hello "));

        System.out.println("World");
    }
複製代碼

呵呵,執行結果誰都知道,顯而易見架構

可是小老弟,有沒有發現這個程序一直都沒有結束源碼分析

呢?明明這個任務都已經跑完了呀~學習

結論

開始了嗎?很差意思已經結束了,嘻嘻,大過年的不賣關子,咱們直接公佈答案,形成不退出的緣由是這樣:this

  • 你醜
  • 線程池的建立的時候,第一次 submit 操做會建立 Worker 線程(負責去拿任務處理),該線程裏寫了一個死循環,因此這個 Worker 線程不會死
  • Worker 線程在建立的時候,被設置成了 非守護線程 , thread.setDaemon(false)
  • 早在 JDK1.5 的時候,就規定了當全部非守護線程退出時, JVM 纔會退出, Main 方法主線程和 Worker 線程都是非守護線程,因此不會死。

下面咱們會就上面幾個問題,每個問題進行源碼分析,感興趣的看官老爺能夠繼續,看看又不會掉髮(逃spa

源碼分析

爲何Worker線程不會死

夢開始的地方先從初始化開始線程

//該方法利用多臺實例化了一個ThreadPoolExecutor線程池,該線程池繼承了一個抽象類AbstractExecutorService
ExecutorService service = Executors.newFixedThreadPool(10);
//調用了ThreadPoolExecutor.submit方法也就是父類的AbstractExecutorService.submit,該方法內部會去調用execute()方法
service.submit(() -> System.out.println("Hello "));
複製代碼

因而咱們定位到 ThreadPoolExecutor 類的 execute 方法,我截取了部分以下, 注意代碼中我打註釋的地方code

public void execute(Runnable command) {
    ...
        //若是工做線程尚未超過核心線程數
        if (workerCountOf(c) < corePoolSize) {
            //去添加工做線程
            if (addWorker(command, true))
                return;
        }
    ...
複製代碼

線程池把每個運行任務的工做線程抽象成了 Worker ,咱們定位到內部 addWorker 方法視頻

...
            //新建一個Worker
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                //下面的操做是將線程添加到工做線程集合裏
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //若是添加成功的話
                if (workerAdded) {
                    //把工做線程跑起來
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;

複製代碼

這時候一個工做線程也就跑起來了,能夠去執行任務了,咱們定位到 ThreadPoolExecutor 的內部類 Worker 的 run 方法裏blog

//該類調用了runWorker方法
public void run() {
            runWorker(this);
        }
        
複製代碼
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //主要看這個while,會看這個Worker有沒有任務,若是沒有就會去取,這裏是一個死循環,而後咱們定位到getTask()方法,看他是怎麼取任務的
            while (task != null || (task = getTask()) != null) {
                w.lock();
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
            ...
               
    }
複製代碼

這裏解釋了,工做線程其實不會死(超時時間不在本期範圍內),咱們繼續定位到內部的 getTask() 方法,看他是怎麼取任務的繼承

private Runnable getTask() {
            ...
            //有沒有設置核心線程超時時間(默認沒有)當前工做的線程數大於了線程池的核心線城市
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            ...
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    //調用workQueue的Take方法,WorkQueue默認是一個BlockingQueue,因此調用take方法會致使當前工做線程阻塞掉,指到拿到
                    workQueue.take();
                //若是拿到任務就返回
                if (r != null)
                    return r;
                timedOut = true;
                ...
複製代碼

小結:

這裏想說的有兩點:

  • 工做線程不會死(不設置線程存活時間,默認狀況下),會一直拿任務,因此工做線程會一直活着
  • 工做線程拿任務的時候,默認狀況下,由於用的是 BlockingQueue 的 take() 拿不到任務會阻塞

Worker線程如何被設置成非守護線程

首先咱們來到 ThreadPoolExecutor 的構造方法裏

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
複製代碼

構造器裏傳入了一個 ThreadFactory 也就是 Executors.defaultThreadFactory() ,用來產生工做線程,一步一步的點進去咱們會定位到 Executors 內部類 DefaultThreadFactory 的 newThread 方法在此我向你們推薦一個架構學習交流裙。交流學習裙號:687810532,裏面會分享一些資深架構師錄製的視頻錄像

public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            //關鍵代碼是這裏,把線程設置成了非守護線程
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
複製代碼

而後咱們看 ThreadPoolExector 方法去 new Worker()的時候

Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            //這裏的ThreadPool,就是上面提到的那個生產非守護線程的線程工廠
            this.thread = getThreadFactory().newThread(this);
        }
複製代碼

看上面的 註釋下面的內容 ,爲何是非守護線程就真相大白了。

爲何要等到非守護線程所有結束的時候,JVM纔會退出?

相關文章
相關標籤/搜索