Java concurrency線程池之線程池原理(二)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
線程池示例
在分析線程池之前,先看一個(gè)簡(jiǎn)單的線程池示例。
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class ThreadPoolDemo1 {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池
ExecutorService pool = Executors.newFixedThreadPool(2);
// 創(chuàng)建實(shí)現(xiàn)了Runnable接口對(duì)象,Thread對(duì)象當(dāng)然也實(shí)現(xiàn)了Runnable接口
Thread ta = new MyThread();
Thread tb = new MyThread();
Thread tc = new MyThread();
Thread td = new MyThread();
Thread te = new MyThread();
// 將線程放入池中進(jìn)行執(zhí)行
pool.execute(ta);
pool.execute(tb);
pool.execute(tc);
pool.execute(td);
pool.execute(te);
// 關(guān)閉線程池
pool.shutdown();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+ " is running.");
}
}
運(yùn)行結(jié)果:
pool-1-thread-1 is running. pool-1-thread-2 is running. pool-1-thread-1 is running. pool-1-thread-2 is running. pool-1-thread-1 is running.
示例中,包括了線程池的創(chuàng)建,將任務(wù)添加到線程池中,關(guān)閉線程池這3個(gè)主要的步驟。稍后,我們會(huì)從這3個(gè)方面來(lái)分析ThreadPoolExecutor。
線程池源碼分析
(一) 創(chuàng)建“線程池”
下面以newFixedThreadPool()介紹線程池的創(chuàng)建過(guò)程。
1. newFixedThreadPool()
newFixedThreadPool()在Executors.java中定義,源碼如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
說(shuō)明:newFixedThreadPool(int nThreads)的作用是創(chuàng)建一個(gè)線程池,線程池的容量是nThreads。
newFixedThreadPool()在調(diào)用ThreadPoolExecutor()時(shí),會(huì)傳遞一個(gè)LinkedBlockingQueue()對(duì)象,而LinkedBlockingQueue是單向鏈表實(shí)現(xiàn)的阻塞隊(duì)列。在線程池中,就是通過(guò)該阻塞隊(duì)列來(lái)實(shí)現(xiàn)"當(dāng)線程池中任務(wù)數(shù)量超過(guò)允許的任務(wù)數(shù)量時(shí),部分任務(wù)會(huì)阻塞等待"。
關(guān)于LinkedBlockingQueue的實(shí)現(xiàn)細(xì)節(jié),讀者可以參考"Java多線程系列--“JUC集合”08之 LinkedBlockingQueue"。
2. ThreadPoolExecutor()
ThreadPoolExecutor()在ThreadPoolExecutor.java中定義,源碼如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
說(shuō)明:該函數(shù)實(shí)際上是調(diào)用ThreadPoolExecutor的另外一個(gè)構(gòu)造函數(shù)。該函數(shù)的源碼如下:
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;
// 線程池的等待隊(duì)列
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
// 線程工廠對(duì)象
this.threadFactory = threadFactory;
// 拒絕策略的句柄
this.handler = handler;
}
說(shuō)明:在ThreadPoolExecutor()的構(gòu)造函數(shù)中,進(jìn)行的是初始化工作。
corePoolSize, maximumPoolSize, unit, keepAliveTime和workQueue這些變量的值是已知的,它們都是通過(guò)newFixedThreadPool()傳遞而來(lái)。下面看看threadFactory和handler對(duì)象。
2.1 ThreadFactory
線程池中的ThreadFactory是一個(gè)線程工廠,線程池創(chuàng)建線程都是通過(guò)線程工廠對(duì)象(threadFactory)來(lái)完成的。
上面所說(shuō)的threadFactory對(duì)象,是通過(guò) Executors.defaultThreadFactory()返回的。Executors.java中的defaultThreadFactory()源碼如下:
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
defaultThreadFactory()返回DefaultThreadFactory對(duì)象。Executors.java中的DefaultThreadFactory()源碼如下:
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
// 提供創(chuàng)建線程的API。
public Thread newThread(Runnable r) {
// 線程對(duì)應(yīng)的任務(wù)是Runnable對(duì)象r
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
// 設(shè)為“非守護(hù)線程”
if (t.isDaemon())
t.setDaemon(false);
// 將優(yōu)先級(jí)設(shè)為“Thread.NORM_PRIORITY”
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
說(shuō)明:ThreadFactory的作用就是提供創(chuàng)建線程的功能的線程工廠。
它是通過(guò)newThread()提供創(chuàng)建線程功能的,下面簡(jiǎn)單說(shuō)說(shuō)newThread()。newThread()創(chuàng)建的線程對(duì)應(yīng)的任務(wù)是Runnable對(duì)象,它創(chuàng)建的線程都是“非守護(hù)線程”而且“線程優(yōu)先級(jí)都是Thread.NORM_PRIORITY”。
2.2 RejectedExecutionHandler
handler是ThreadPoolExecutor中拒絕策略的處理句柄。所謂拒絕策略,是指將任務(wù)添加到線程池中時(shí),線程池拒絕該任務(wù)所采取的相應(yīng)策略。
線程池默認(rèn)會(huì)采用的是defaultHandler策略,即AbortPolicy策略。在AbortPolicy策略中,線程池拒絕任務(wù)時(shí)會(huì)拋出異常!
defaultHandler的定義如下:
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
AbortPolicy的源碼如下:
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
// 拋出異常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
(二) 添加任務(wù)到“線程池”
1. execute()
execute()定義在ThreadPoolExecutor.java中,源碼如下:
public void execute(Runnable command) {
// 如果任務(wù)為null,則拋出異常。
if (command == null)
throw new NullPointerException();
// 獲取ctl對(duì)應(yīng)的int值。該int值保存了"線程池中任務(wù)的數(shù)量"和"線程池狀態(tài)"信息
int c = ctl.get();
// 當(dāng)線程池中的任務(wù)數(shù)量 < "核心池大小"時(shí),即線程池中少于corePoolSize個(gè)任務(wù)。
// 則通過(guò)addWorker(command, true)新建一個(gè)線程,并將任務(wù)(command)添加到該線程中;然后,啟動(dòng)該線程從而執(zhí)行任務(wù)。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 當(dāng)線程池中的任務(wù)數(shù)量 >= "核心池大小"時(shí),
// 而且,"線程池處于允許狀態(tài)"時(shí),則嘗試將任務(wù)添加到阻塞隊(duì)列中。
if (isRunning(c) && workQueue.offer(command)) {
// 再次確認(rèn)“線程池狀態(tài)”,若線程池異常終止了,則刪除任務(wù);然后通過(guò)reject()執(zhí)行相應(yīng)的拒絕策略的內(nèi)容。
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
// 否則,如果"線程池中任務(wù)數(shù)量"為0,則通過(guò)addWorker(null, false)嘗試新建一個(gè)線程,新建線程對(duì)應(yīng)的任務(wù)為null。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 通過(guò)addWorker(command, false)新建一個(gè)線程,并將任務(wù)(command)添加到該線程中;然后,啟動(dòng)該線程從而執(zhí)行任務(wù)。
// 如果addWorker(command, false)執(zhí)行失敗,則通過(guò)reject()執(zhí)行相應(yīng)的拒絕策略的內(nèi)容。
else if (!addWorker(command, false))
reject(command);
}
說(shuō)明:execute()的作用是將任務(wù)添加到線程池中執(zhí)行。它會(huì)分為3種情況進(jìn)行處理:
情況1 -- 如果"線程池中任務(wù)數(shù)量" < "核心池大小"時(shí),即線程池中少于corePoolSize個(gè)任務(wù);此時(shí)就新建一個(gè)線程,并將該任務(wù)添加到線程中進(jìn)行執(zhí)行。
情況2 -- 如果"線程池中任務(wù)數(shù)量" >= "核心池大小",并且"線程池是允許狀態(tài)";此時(shí),則將任務(wù)添加到阻塞隊(duì)列中阻塞等待。在該情況下,會(huì)再次確認(rèn)"線程池的狀態(tài)",如果"第2次讀到的線程池狀態(tài)"和"第1次讀到的線程池狀態(tài)"不同,則從阻塞隊(duì)列中刪除該任務(wù)。
情況3 -- 非以上兩種情況。在這種情況下,嘗試新建一個(gè)線程,并將該任務(wù)添加到線程中進(jìn)行執(zhí)行。如果執(zhí)行失敗,則通過(guò)reject()拒絕該任務(wù)。
2. addWorker()
addWorker()的源碼如下:
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 更新"線程池狀態(tài)和計(jì)數(shù)"標(biāo)記,即更新ctl。
for (;;) {
// 獲取ctl對(duì)應(yīng)的int值。該int值保存了"線程池中任務(wù)的數(shù)量"和"線程池狀態(tài)"信息
int c = ctl.get();
// 獲取線程池狀態(tài)。
int rs = runStateOf(c);
// 有效性檢查
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 獲取線程池中任務(wù)的數(shù)量。
int wc = workerCountOf(c);
// 如果"線程池中任務(wù)的數(shù)量"超過(guò)限制,則返回false。
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 通過(guò)CAS函數(shù)將c的值+1。操作失敗的話,則退出循環(huán)。
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 檢查"線程池狀態(tài)",如果與之前的狀態(tài)不同,則從retry重新開(kāi)始。
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;
// 添加任務(wù)到線程池,并啟動(dòng)任務(wù)所在的線程。
try {
final ReentrantLock mainLock = this.mainLock;
// 新建Worker,并且指定firstTask為Worker的第一個(gè)任務(wù)。
w = new Worker(firstTask);
// 獲取Worker對(duì)應(yīng)的線程。
final Thread t = w.thread;
if (t != null) {
// 獲取鎖
mainLock.lock();
try {
int c = ctl.get();
int rs = runStateOf(c);
// 再次確認(rèn)"線程池狀態(tài)"
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 將Worker對(duì)象(w)添加到"線程池的Worker集合(workers)"中
workers.add(w);
// 更新largestPoolSize
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
// 釋放鎖
mainLock.unlock();
}
// 如果"成功將任務(wù)添加到線程池"中,則啟動(dòng)任務(wù)所在的線程。
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
// 返回任務(wù)是否啟動(dòng)。
return workerStarted;
}
說(shuō)明:
addWorker(Runnable firstTask, boolean core) 的作用是將任務(wù)(firstTask)添加到線程池中,并啟動(dòng)該任務(wù)。
core為true的話,則以corePoolSize為界限,若"線程池中已有任務(wù)數(shù)量>=corePoolSize",則返回false;core為false的話,則以maximumPoolSize為界限,若"線程池中已有任務(wù)數(shù)量>=maximumPoolSize",則返回false。
addWorker()會(huì)先通過(guò)for循環(huán)不斷嘗試更新ctl狀態(tài),ctl記錄了"線程池中任務(wù)數(shù)量和線程池狀態(tài)"。
更新成功之后,再通過(guò)try模塊來(lái)將任務(wù)添加到線程池中,并啟動(dòng)任務(wù)所在的線程。
從addWorker()中,我們能清晰的發(fā)現(xiàn):線程池在添加任務(wù)時(shí),會(huì)創(chuàng)建任務(wù)對(duì)應(yīng)的Worker對(duì)象;而一個(gè)Workder對(duì)象包含一個(gè)Thread對(duì)象。(01) 通過(guò)將Worker對(duì)象添加到"線程的workers集合"中,從而實(shí)現(xiàn)將任務(wù)添加到線程池中。 (02) 通過(guò)啟動(dòng)Worker對(duì)應(yīng)的Thread線程,則執(zhí)行該任務(wù)。
3. submit()
補(bǔ)充說(shuō)明一點(diǎn),submit()實(shí)際上也是通過(guò)調(diào)用execute()實(shí)現(xiàn)的,源碼如下:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
(三) 關(guān)閉“線程池”
shutdown()的源碼如下:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
// 獲取鎖
mainLock.lock();
try {
// 檢查終止線程池的“線程”是否有權(quán)限。
checkShutdownAccess();
// 設(shè)置線程池的狀態(tài)為關(guān)閉狀態(tài)。
advanceRunState(SHUTDOWN);
// 中斷線程池中空閑的線程。
interruptIdleWorkers();
// 鉤子函數(shù),在ThreadPoolExecutor中沒(méi)有任何動(dòng)作。
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
// 釋放鎖
mainLock.unlock();
}
// 嘗試終止線程池
tryTerminate();
}
說(shuō)明:shutdown()的作用是關(guān)閉線程池。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java使用RestTemplate封裝post請(qǐng)求方式
這篇文章主要介紹了java使用RestTemplate封裝post請(qǐng)求方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
java中樂(lè)觀鎖與悲觀鎖區(qū)別及使用場(chǎng)景分析
本文主要介紹了java中樂(lè)觀鎖與悲觀鎖區(qū)別及使用場(chǎng)景分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
java 2d畫(huà)圖示例分享(用java畫(huà)圖)
這篇文章主要介紹了java 2D畫(huà)圖示例(用java畫(huà)圖),需要的朋友可以參考下2014-04-04
Java窗體中關(guān)于默認(rèn)布局管理器容易踩的坑及解決
這篇文章主要介紹了Java窗體中關(guān)于默認(rèn)布局管理器容易踩的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
springboot集成sensitive-word實(shí)現(xiàn)敏感詞過(guò)濾的兩種方案
敏感詞過(guò)濾通常是指從文本中檢測(cè)并移除或替換掉被認(rèn)為是不適當(dāng)、冒犯性或違反特定社區(qū)準(zhǔn)則的詞匯,這篇文章主要介紹了springboot集成sensitive-word實(shí)現(xiàn)敏感詞過(guò)濾,需要的朋友可以參考下2024-08-08
SpringBoot實(shí)現(xiàn)RAS+AES自動(dòng)接口解密
本文主要介紹了SpringBoot實(shí)現(xiàn)RAS+AES自動(dòng)接口解密,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
MyBatis-Plus?updateById更新不了空字符串或null的解決方法
本文主要介紹了MyBatis-Plus?updateById更新不了空字符串或null的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03

