Java併發之線程池詳解

Java線程池有哪些狀態?如何切換?java

線程池的種類有哪些?數組

建立線程池須要哪些參數?含義?緩存

將任務添加到線程池的運行流程?ide

線程池怎麼重用線程?操作系統

線程池如何關閉?線程

首先線程池有哪些狀態以及如何切換

來搞一下源碼:ThreadPoolExecutor.javacode

private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

各類狀態的說明在源碼的註釋中都有,咱們來看一下:對象

  • The runState provides the main lifecycle control, taking on values:接口

    RUNNING:  Accept new tasks and process queued tasks
     SHUTDOWN: Don't accept new tasks, but process queued tasks
     STOP:     Don't accept new tasks, don't process queued tasks,and interrupt in-progress tasks
     TIDYING:  All tasks have terminated, workerCount is zero,
      the thread transitioning to state TIDYING will run the terminated() hook method
     TERMINATED: terminated() has completed

處於RUNNING狀態的線程池能夠接收新的任務,也可以處理阻塞隊列裏面的任務。隊列

SHUTDOWN:不能接受新的任務,能夠處理阻塞隊列裏面的任務。

STOP:不能接受新的任務,也不會處理阻塞隊列的任務。並且中斷正在處理的任務。

TIDYING:全部的任務都被終止,線程池中工做線程的數量爲0,將要調用terminated()方法。

TERMINATED: terminated()方法調用完畢,終止狀態。

狀態之間進行切換:

RUNNING -> SHUTDOWN
    On invocation of shutdown(), perhaps implicitly in finalize()
(RUNNING or SHUTDOWN) -> STOP
     On invocation of shutdownNow()
SHUTDOWN -> TIDYING
     When both queue and pool are empty
STOP -> TIDYING
     When pool is empty
TIDYING -> TERMINATED
     When the terminated() hook method has completed

RUNNING切換到SHUTDOWN狀態:調用shutdown()方法後切換到SHUTDOWN狀態。

RUNNING、SHUTDOWN切換到STOP狀態:調用shutdownNow()方法切換到STOP狀態

SHUTDOWN切換到TIDYING狀態:在阻塞隊列和線程池爲空的狀況下切換到TIDYING狀態。

STOP切換到TIDYING狀態:線程池爲空時切換到該狀態。

TIDYING切換到TERMINATED狀態: 調用terminated()方法切換到該狀態。

建立線程池須要哪些參數?參數的含義?

源碼中建立ThreadPoolExecutor的構造方法是這樣的:

public ThreadPoolExecutor
    int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    ThreadFactory threadFactory,
    RejectedExecutionHandler handler) {
corePoolSize:
核心線程的數量,當一個任務條件到線程池中執行時,只要線程的數量達不到corePoolSize的數量,就會建立新的線程,直到線程數量達到corePoolSize的大小,再也不建立,將新的任務放到阻塞隊列中等待執行。
maximumPoolSize:線程池中容許建立最大線程數量,若是阻塞隊列已滿,建立的線程數量小於maximumPoolSize的話,會建立新的線程來處理阻塞隊列中的任務。
keepAliveTime:線程活動的保持時間,當線程數量大於corePoolSize時,核心線程以外的線程空閒時間,大於該時間將會被回收。

unit:keepAliveTime的單位。

workQueue: 阻塞隊列,當線程池中的線程數量爲corePoolSize時,新來的任務會放到該阻塞隊列中,有四種阻塞隊列:

ArrayBlockingQueue:基於數組的阻塞隊列
LinkedBlockingQueue:基於鏈表的阻塞隊列
SychronusBlockingQueue:不存儲元素的阻塞隊列(沒見過)
PriorityBlockingQueue:基於優先級的阻塞隊列

threadFactory:建立線程的工廠

handler: 當阻塞隊列滿了,沒有空閒線程的狀況下,線程中的數量已經達到了最大數目,必須採起一種策略來處理新來的任務。主要有四種策略:AbortPolicy(直接拋出異常)、CallerRunsPolicy(使用調用者的線程處理)、DiscardOldsPolciy(丟棄阻塞隊列中一個任務,處理當前任務)、Discard直接丟棄。

線程池的運行流程?

線程池對象有兩個方法用來執行線程: submit()和execute()方法。

區別是submit()方法能夠傳入一個實現Callable()接口的對象,在當前任務結束的時候能返回一個Future對象來獲取任務的返回值。 submit()方法仍是調用了execute()方法

public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

下面咱們重點來看一下execute()方法:

int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);

首先判斷當前線程的數量與核心線程數量比較,若是小於核心線程的數量,直接建立一個新的worker線程

若是當前線程數量大於corePoolSize的數量,嘗試添加任務到阻塞隊列裏面,而後第二次檢查線程的數量,若是線程池狀態不在RUNNING,直接移除,同時拒絕當前請求的任務,若是狀態爲RUNNING且線程的數量爲0,建立一個新的線程。

若是當前狀態不是RUNNING,則嘗試建立一個新的worker來處理任務,若是建立失敗,拒絕當前任務。

線程運行的四個階段: 1.poolSize<corePoolSize 當前線程數量小於核心線程數量,直接建立線程來處理

2.poolSize=corePoolSize,並且此時阻塞隊列沒有滿,將此任務添加到阻塞隊列裏面,若是此時存在工做線程(非核心線程),由工做線程來處理阻塞隊列中的任務,若是工做線程數量爲0,則會建立工做線程來處理 3. poolSize=corePoolSize 而且此時阻塞隊列滿了,直接建立新的工做線程來處理當前任務

4.poolSize=maxmumPoolSize:此時阻塞隊列也滿了,就會觸發拒絕機制,具體什麼策略由傳入的RejectExceptionHandler決定

重用線程

線程池重用線程,主要是在執行worker線程時候去阻塞隊列裏面拿任務,不斷的去拿任務交給線程來執行,達到重用的目的,直到getTask爲null爲止。

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 (task != null || (task = getTask()) != null) {

線程池關閉

調用shutdown()和shutdownNow()方法

Java中經常使用線程池:

<p>newSingleThreadExecutor:建立一個單線程的線程池。這個線程池只有一個線程在工做,也就是至關於單線程串行執行全部任務。若是這個惟一的線程由於異常結束,那麼會有一個新的線程來替代它。此線程池保證全部任務的執行順序按照任務的提交順序執行。</p>
<p>newFixedThreadPool:建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,若是某個線程由於執行異常而結束,那麼線程池會補充一個新線程。</p>
<p>newCachedThreadPool:建立一個可緩存的線程池。若是線程池的大小超過了處理任務所須要的線程,那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增長時,此線程池又能夠智能的添加新線程來處理任務。此線程池不會對線程池大小作限制,線程池大小徹底依賴於操做系統(或者說JVM)可以建立的最大線程大小。</p>
<p>newScheduledThreadPool:建立一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。</p>
<p>newSingleThreadExecutor:建立一個單線程的線程池。此線程池支持定時以及週期性執行任務的需求。</p>
相關文章
相關標籤/搜索