深入淺出線程池



一、線程

1、什麼是線程

線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際 運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線 程並行執行不同的任務。

2、如何創建線程

2.1、JAVA中創建線程

  
  
  
/** * 繼承Thread類,重寫run方法 */class MyThread extends Thread {    @Override    public void run() {        System.out.println("myThread..." + Thread.currentThread().getName());} }
/** * 實現Runnable接口,實現run方法 */class MyRunnable implements Runnable { @Override public void run() { System.out.println("MyRunnable..." + Thread.currentThread().getName());} }
/** * 實現Callable接口,指定返回類型,實現call方法 */class MyCallable implements Callable<String> { @Override public String call() throws Exception { return "MyCallable..." + Thread.currentThread().getName();} }

2.2、測試一下

  
  
  
public static void main(String[] args) throws Exception {    MyThread thread = new MyThread();    thread.run();   //myThread...main    thread.start(); //myThread...Thread-0        MyRunnable myRunnable = new MyRunnable();    Thread thread1 = new Thread(myRunnable);    myRunnable.run();   //MyRunnable...main    thread1.start();    //MyRunnable...Thread-1        MyCallable myCallable = new MyCallable();    FutureTask<String> futureTask = new FutureTask<>(myCallable);    Thread thread2 = new Thread(futureTask);    thread2.start();    System.out.println(myCallable.call());  //MyCallable...main    System.out.println(futureTask.get());   //MyCallable...Thread-2
}

2.3、問題

既然我們創建了線程,那爲何我們直接調用方法和我們調用start()方法的結果不同?new Thread() 是否真實創建了線程?

2.4、問題分析

我們直接調用方法,可以看到是執行的主線程,而調用start()方法就是開啓了新線程,那說明new Thread()並沒有創建線程,而是在start()中創建了線程。
那我們看下Thread類start()方法:
  
  
  
class Thread implements Runnable { //Thread類實現了Runnalbe接口,實現了run()方法         private Runnable target;
public synchronized void start() { ...
boolean started = false; try { start0(); //可以看到,start()方法真實的調用時start0()方法 started = true; } finally { ... } } private native void start0(); //start0()是一個native方法,由JVM調用底層操作系統,開啓一個線程,由操作系統過統一調度
@Override public void run() { if (target != null) { target.run(); //操作系統在執行新開啓的線程時,回調Runnable接口的run()方法,執行我們預設的線程任務
} } }

2.5、總結

  • JAVA不能直接創建線程執行任務,而是通過創建Thread對象調用操作系統開啓線程,在由操作系 統回調Runnable接口的run()方法執行任務;
  • 實現Runnable的方式,將線程實際要執行的回調任務單獨提出來了,實現線程的啓動與回調任務 解耦;
  • 實現Callable的方式,通過Future模式不但將線程的啓動與回調任務解耦,而且可以 在執行完成後 獲取到執行的結果;


二、多線程

1、什麼是多線程

多線程(multithreading),是指從軟件或者硬件上實現多個線程併發執行的技術。同一個線程只能處理完一個任務再處理下一個任務,有時我們需要多個任務同時處理,這時,我們就需要創建多 個線程來同時處理任務。

2、多線程有什麼好處

2.1、串行處理

  
  
  
public static void main(String[] args) throws Exception {    System.out.println("start...");    long start = System.currentTimeMillis();    for (int i = 0; i < 5; i++) {        Thread.sleep(2000);  //每個任務執行2秒         System.out.println("task done..."); //處理執行結果    }    long end = System.currentTimeMillis();    System.out.println("end...,time = "  + (end - start));}//執行結果start...task done...task done...task done...task done...task done... end...,time = 10043

2.2、並行處理

  
  
  
public static void main(String[] args) throws Exception {    System.out.println("start...");    long start = System.currentTimeMillis();    List<Future> list = new ArrayList<>();
for (int i = 0; i < 5; i++) { Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(2000); //每個任務執行2秒 return "task done..."; }
}; FutureTask task = new FutureTask(callable); list.add(task); new Thread(task).start();
} list.forEach(future -> { try { System.out.println(future.get()); //處理執行結果 } catch (Exception e) { } }); long end = System.currentTimeMillis(); System.out.println("end...,time = " + (end - start));
} //執行結果 start... task done... task done... task done... task done... task done... end...,time = 2005

2.3、總結

  • 多線程可以把一個任務拆分爲幾個子任務,多個子任務可以併發執行,每一個子任務就是一個線程。
  • 多線程是爲了同步完成多項任務,不是爲了提高運行效率,而是爲了提高資源使用效率來提高 系統 的效率。

2.4、多線程的問題

上面示例中我們可以看到,如果每來一個任務,我們就創建一個線程,有很多任務的情況下,我們 會創建大量的線程,可能會導致系統資源的耗盡。同時,我們知道線程的執行是需要搶佔CPU資源 的,那如果有太多的線程,就會導致大量時間用在線程切換的開銷上。
再有,每來一個任務都需要創建一個線程,而創建一個線程需要調用操作系統底層方法,開銷較 大,而線程執行完成後就被回收了。在需要大量線程的時候,創建線程的時間就花費不少了。


三、線程池

1、如何設計一個線程池

由於多線程的開發存在上述的一些問題,那我們是否可以設計一個東西來避免這些問題呢?當然可以! 線程池就是爲了解決這些問題而生的。那我們該如何設計一個線程池來解決這些問題呢?或者說,一個線程池該具備什麼樣的功能?

1.1、線程池基本功能

  • 多線程會創建大量的線程耗盡資源,那線程池應該對線程數量有所限制,可以保證不會耗盡系統資 源;
  • 每次創 建新的線程會增加創建時的開銷,那線程池應該減少線程的創建,儘量複用已創建好的線 程;

1.2、線程池面臨問題

  • 我們知道線程在執行完自己的任務後就會被回收,那我們如何複用線程?
  • 我們指定了線程的最大 數量,當任務數超出線程數時,我們該如何處理?

1.3、創新源於生活

先假設一個場景:假設我們是一個物流公司的管理人員,要配送的貨物就是我們的任務,貨車就是 我們配送工具,我們當然不能有多少貨物就準備多少貨車。那當顧客源源不斷的將貨物交給我們配送,我們該如何管理才能讓公司經營的最好呢?
  • 最開始貨物來的時候,我們還沒有貨車,每批要運輸的貨物我們都要購買一輛車來運輸;
  • 當貨車運輸完成後,暫時還沒有下一批貨物到達,那貨車就在倉庫停着,等有貨物來了立馬就可以 運輸;
  • 當我們有了一定數量的車後,我們認爲已經夠用了,那後面就不再買車了,這時要是由新的貨物來 了,我們就會讓貨物先放倉庫,等有車回來再配送;
  • 當618大促來襲,要配送的貨物太多,車都在路上,倉庫也都放滿了,那怎麼辦呢?我們就選擇臨 時租一些車來幫忙配送,提高配送的效率;
  • 但是貨物還是太多,我們增加了臨時的貨車,依舊配送不過來,那這時我們就沒辦法了,只能讓發貨的客戶排隊等候或者乾脆不接收了;
  • 大促 圓滿完成後,累計的貨物已經配送完成了,爲了降低成本,我們就將臨時租的車都還了;

1.4、技術源於創新

基於上述場景,物流公司就是我們的線程池、貨物就是我們的線程任務、貨車就是我們的線程。我們如何設計公司的管理貨車的流程,就應該如何設計線程池管理線程的流程。
  • 當任務進來我們還沒有線程時,我們就該創建線程執行任務;
  • 當線程任務執行完成後,線程不釋放,等着下一個任務進來後接着執行;
  • 當創建的線程數量達到一定量後,新來的任務我們存起來等待空閒線程執行,這就要求線程池有個 存任務的容器;
  • 當容器存滿後,我們需要增加一些臨時的線程來提高處理效率;
  • 當增加臨時線程後依舊處理不了的任務,那就應該將此任務拒絕;
  • 當所有 任務執行完成後,就應該將臨時的線程釋放掉,以免增加不必要的開銷;

2、線程池具體分析

上文中,我們講了該如何設計一個線程池,下面我們看看大神是如何設計的;

2.1、 JAVA中的線程池是如何設計的

2.1.1、 線程池設計

看下線程池中的屬性,瞭解線程池的設計。
  
  
  
public class ThreadPoolExecutor extends AbstractExecutorService {
//線程池的打包控制狀態,用高3位來表示線程池的運行狀態,低29位來表示線程池中工作線程的數量 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); //值爲29,用來表示偏移量 private static final int COUNT_BITS = Integer.SIZE - 3;
//線程池的最大容量 private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//線程池的運行狀態,總共有5個狀態,用高3位來表示 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; //所有任務都已終止, 工作線程數量爲0,即將要執行terminated()鉤子方法
private static final int TERMINATED = 3 << COUNT_BITS; // terminated()方法已經執行結束
//任務緩存隊列,用來存放等待執行的任務 private final BlockingQueue<Runnable> workQueue;
//全局鎖,對線程池狀態等屬性修改時需要使用這個鎖 private final ReentrantLock mainLock = new ReentrantLock();
//線程池中工作線程的集合,訪問和修改需要持有全局鎖 private final HashSet<Worker> workers = new HashSet<Worker>();
// 終止條件 private final Condition termination = mainLock.newCondition();
//線程池中曾經出現過的最大線程數 private int largestPoolSize; //已完成任務的數量 private long completedTaskCount; //線程工廠 private volatile ThreadFactory threadFactory; //任務拒絕策略 private volatile RejectedExecutionHandler handler;
//線程存活時間 private volatile long keepAliveTime;
//是否允許核心線程超時 private volatile boolean allowCoreThreadTimeOut;
//核心池大小,若allowCoreThreadTimeOut被設置,核心線程全部空閒超時被回收的情況下會爲0 private volatile int corePoolSize;
//最大池大小,不得超過CAPACITY private volatile int maximumPoolSize; //默認的任務拒絕策略 private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
//運行權限相關 private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread");
... }
小結一下:以上線程池的設計可以看出,線程池的功能還是很完善的。
  • 提供了線程創建、數量及存活時間等的管理;
  • 提供了線程池狀態流轉的管理;
  • 提供了任務緩存的各種容器;
  • 提供了多餘任務的處理機制;
  • 提供了簡單的統計功 能;

2.1.2、線程池構造函數

  
  
  
//構造函數 public ThreadPoolExecutor(int corePoolSize, //核心線程數                            int maximumPoolSize, //最大允許線程數                            long keepAliveTime, //線程存活時間                            TimeUnit unit, //存活時間單位                            BlockingQueue<Runnable> workQueue, //任務緩存隊列                           ThreadFactory threadFactory, //線程工廠                            RejectedExecutionHandler handler) { //拒絕策略     if (corePoolSize < 0 ||        maximumPoolSize <= 0 ||        maximumPoolSize < corePoolSize ||        keepAliveTime < 0)        throw new IllegalArgumentException();            if (workQueue == null || threadFactory == null || handler == null)        throw new NullPointerException();            this.corePoolSize = corePoolSize;    this.maximumPoolSize = maximumPoolSize;    this.workQueue = workQueue;    this.keepAliveTime = unit.toNanos(keepAliveTime);    this.threadFactory = threadFactory;    this.handler = handler;}
小結一下:
  • 構造函數告訴了我們可以怎樣去適用線程池,線程池的哪些特性是我們可以控制的;

2.1.3、線程池執行

2.1.3.1、提交任務方法
  • public void execute(Runnable command);
  • Future<?> submit(Runnable task);
  • Future submit(Runnable task, T result);
  • Future submit(Callable task);
  
  
  
public Future<?> submit(Runnable task) {        if (task == null) throw new NullPointerException();        RunnableFuture<Void> ftask = newTaskFor(task, null);        execute(ftask);        return ftask;}
可以看到submit方法的底層調用的也是execute方法,所以我們這裏只分析execute方法;
  
  
  
    public void execute(Runnable command) {        if (command == null)            throw new NullPointerException();                int c = ctl.get();        //第一步:創建核心線程        if (workerCountOf(c) < corePoolSize) {  //worker數量小於corePoolSize            if (addWorker(command, true))       //創建worker                return;            c = ctl.get();        }        //第二步:加入緩存隊列        if (isRunning(c) && workQueue.offer(command)) { //線程池處於RUNNING狀態,將任務加入workQueue任務緩存隊列            int recheck = ctl.get();                if (! isRunning(recheck) && remove(command))    //雙重檢查,若線程池狀態關閉了,移除任務                reject(command);            else if (workerCountOf(recheck) == 0)       //線程池狀態正常,但是沒有線程了,創建worker                addWorker(null, false);        }        //第三步:創建臨時線程        else if (!addWorker(command, false))            reject(command);    }
小結一下:execute()方法主要功能:
  • 核心線程數量不足就創建核心線程;
  • 核心線程滿了就加入緩存隊列;
  • 緩存隊列滿了就增加非核心線程;
  • 非核心線 程也滿了就拒絕任務;
2.1.3.2、創建線程
private boolean addWorker(Runnable firstTask, boolean core) {        retry:        for (;;) {            int c = ctl.get();            int rs = runStateOf(c);
//等價於:rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty()) //線程池已關閉,並且無需執行緩存隊列中的任務,則不創建 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false;
for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) //CAS增加線程數 break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } }
//上面的流程走完,就可以真實開始創建線程了 boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); //這裏創建了線程 final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. 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; }
小結:addWorker()方法主要功能;
  • 增加線程數;
  • 創建線程Worker實例加入線程池;
  • 加入完成開啓線程;
  • 啓動失敗則回滾增加流程;
2.1.3.3、工作線程的實現
    private final class Worker  //Worker類是ThreadPoolExecutor的內部類        extends AbstractQueuedSynchronizer          implements Runnable{                final Thread thread;    //持有實際線程        Runnable firstTask;     //worker所對應的第一個任務,可能爲空        volatile long completedTasks;   //記錄執行任務數
Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } public void run() { runWorker(this); //當前線程調用ThreadPoolExecutor中的runWorker方法,在這裏實現的線程複用 }
...繼承AQS,實現了不可重入鎖... }

   
   
   
小結:工作線程Worker類主要功能;
  • 此類持有一個工作線程,不斷處理拿到的新任務,持有的線程即爲可複用的線程;
  • 此類可看作一個適配類,在run()方法中真實調用runWorker()方法不斷獲取新任務,完成線程複用;
2.1.3.4、線程的複用
final void runWorker(Worker w) {    //ThreadPoolExecutor中的runWorker方法,在這裏實現的線程複用        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) {    //這裏會不斷從任務隊列獲取任務並執行                w.lock();                                //線程是否需要中斷                if ((runStateAtLeast(ctl.get(), STOP) ||                         (Thread.interrupted() &&                      runStateAtLeast(ctl.get(), STOP))) &&                    !wt.isInterrupted())                    wt.interrupt();                try {                    beforeExecute(wt, task);    //執行任務前的Hook方法,可自定義                    Throwable thrown = null;                    try {                        task.run();             //執行實際的任務                    } catch (RuntimeException x) {                        thrown = x; throw x;                    } catch (Error x) {                        thrown = x; throw x;                    } catch (Throwable x) {                        thrown = x; throw new Error(x);                    } finally {                        afterExecute(task, thrown); //執行任務後的Hook方法,可自定義                    }                } finally {                    task = null;    //執行完成後,將當前線程中的任務制空,準備執行下一個任務                    w.completedTasks++;                    w.unlock();                }            }            completedAbruptly = false;        } finally {            processWorkerExit(w, completedAbruptly);    //線程執行完成後的清理工作        }    }
小結:runWorker()方法主要功能;
  • 循環從緩存隊列中獲取新的任務,直到沒有任務爲止;
  • 使用worker持有的線程真實執行任務;
  • 任務都執行完成後的清理工作;
2.1.3.5、隊列中獲取待執行任務
  
  
  
private Runnable getTask() {        boolean timedOut = false;   //標識當前線程是否超時未能獲取到task對象
for (;;) { int c = ctl.get(); int rs = runStateOf(c);
// Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; }
int wc = workerCountOf(c);
// Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) //若線程存活時間超時,則CAS減去線程數量 return null; continue; }
try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //允許超時回收則阻塞等待 workQueue.take(); //不允許則直接獲取,沒有就返回null if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
小結:getTask()方法主要功能;
實際在緩存隊列中獲取待執行的任務;
在這裏管理線程是否要阻塞等待,控制線程的數量;
2.1.3.6、清理工作
private void processWorkerExit(Worker w, boolean completedAbruptly) {        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted            decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; workers.remove(w); //移除執行完成的線程 } finally { mainLock.unlock(); }
tryTerminate(); //每次回收完一個線程後都嘗試終止線程池
int c = ctl.get(); if (runStateLessThan(c, STOP)) { //到這裏說明線程池沒有終止 if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false); //異常終止線程的話,需要在常見一個線程 } }
小結:processWorkerExit()方法主要功能;
  • 真實完成線程池線程的回收;
  • 調用嘗試終止線程池;
  • 保證線程池正常運行;
2.1.3.7、嘗試終止線程池
final void tryTerminate() {        for (;;) {            int c = ctl.get();                        //若線程池正在執行、線程池已終止、線程池還需要執行緩存隊列中的任務時,返回            if (isRunning(c) ||                runStateAtLeast(c, TIDYING) ||                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))                return;                            //執行到這裏,線程池爲SHUTDOWN且無待執行任務 或 STOP 狀態            if (workerCountOf(c) != 0) {                interruptIdleWorkers(ONLY_ONE);     //只中斷一個線程                return;            }
//執行到這裏,線程池已經沒有可用線程了,可以終止了 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { //CAS設置線程池終止 try { terminated(); //執行鉤子方法 } finally { ctl.set(ctlOf(TERMINATED, 0)); //這裏將線程池設爲終態 termination.signalAll(); } return; } } finally { mainLock.unlock(); } // else retry on failed CAS } }
小結:tryTerminate()方法主要功能;
  • 實際嘗試終止線程池;
  • 終止成功則調用鉤子方法,並且將線程池置爲終態。

2.2、JAVA線程池總結

以上通過對JAVA線程池的具體分析我們可以看出,雖然流程看似複雜,但其實有很多內容都是狀態重複校驗、線程安全的保證等內容,其主要的功能與我們前面所提出的設計功能一致,只是額外增加了一些擴展,下面我們簡單整理下線程池的功能;
2.2.1、主要功能
  • 線程數量及存活時間的管理;
  • 待處理任務的存儲功能;
  • 線程複用機制功能;
  • 任務超量的拒絕功能;
2.2.2、擴展功能
  • 簡單的執行結果統計功能;
  • 提供線程執行異常處理機制;
  • 執行前後處理流程自定義;
  • 提供線程創建方式的自定義;
2.2.3、流程總結
以上通過對JAVA線程池任務提交流程的分析我們可以看出,線程池執行的簡單流程如下圖所示;

2.3、JAVA線程池使用

線程池基本使用驗證上述流程:
public static void main(String[] args) throws Exception {                //創建線程池       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(               5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue(5));                //加入4個任務,小於核心線程,應該只有4個核心線程,隊列爲0        for (int i = 0; i < 4; i++) {            threadPoolExecutor.submit(new MyRunnable());        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 4        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0                //再加4個任務,超過核心線程,但是沒有超過核心線程 + 緩存隊列容量,應該5個核心線程,隊列爲3        for (int i = 0; i < 4; i++) {            threadPoolExecutor.submit(new MyRunnable());        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 3                //再加4個任務,隊列滿了,應該5個熱核心線程,隊列5個,非核心線程2個        for (int i = 0; i < 4; i++) {            threadPoolExecutor.submit(new MyRunnable());        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 7        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5                //再加4個任務,核心線程滿了,應該5個熱核心線程,隊列5個,非核心線程5個,最後一個拒絕        for (int i = 0; i < 4; i++) {            try {                threadPoolExecutor.submit(new MyRunnable());            } catch (Exception e) {                e.printStackTrace();    //java.util.concurrent.RejectedExecutionException            }        }        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 10        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5        System.out.println(threadPoolExecutor.getTaskCount());  //共執行15個任務                //執行完成,休眠15秒,非核心線程釋放,應該5個核心線程,隊列爲0        Thread.sleep(1500);        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0                //關閉線程池        threadPoolExecutor.shutdown();    }
-end-

本文分享自微信公衆號 - 京東雲開發者(JDT_Developers)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章