線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際 運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線 程並行執行不同的任務。 ...
一、線程
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();
}
作者:京東零售 秦浩然
來源:京東雲開發者社區 轉載請註明來源