bilibili-Java併發學習筆記19 線程池 ThreadPoolExecutor
基於 java 1.8.0
P52_Java線程池層次體系與設計原則
Executor
ExecutorService
AbstractExecutorService
ThreadPoolExecutor
-> Worker
P53_線程池創建方式與工廠模式的應用
創建 ThreadPoolExecutor 實例
package new_package.thread.p52;
import java.util.concurrent.*;
public class ThreadPoolTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = new ThreadPoolExecutor(10,
10,
1,
TimeUnit.SECONDS,
new LinkedBlockingQueue(),
(r, executor) -> {
});
executorService.execute(() -> {
System.out.println("ThreadPool");
});
Future<String> future = executorService.submit(() -> {
int i = 77;
return "hello " + i;
});
System.out.println(future.get());
executorService.shutdown();
}
}
Executors 工廠模式 創建線程池
package new_package.thread.p52;
import java.util.concurrent.*;
public class ThreadPoolTest2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(() -> System.out.println(Thread.currentThread().getName()));
}
executorService.shutdown();
}
}
P54_線程池構建方式與細節信息解析
ThreadPoolExecutor 的構造參數
- corePoolSize
核心線程數
- maximumPoolSize
最大線程數
-
keepAliveTime
-
TimeUnit
與 keepAliveTime 結合使用 , 當 maximumPoolSize > corePoolSize 時纔有意義
當線程池的線程數量 > corePoolSize
時,且當前任務數並沒有佔滿所有線程池中的線程,等到 keepAliveTime 後,線程將被回收;
- BlockingQueue 阻塞隊列
- ArrayBlockingQueue
- 有界隊列
- 基於數組
- LinkedBlockingQueue
- 有界隊列
- 基於鏈表
- 吞吐量比 ArrayBlockingQueue 高
- PriorityBlockingQueue
- DelayQueue
- SynchronousQueue
- LinkedTransferQueue
- ThreadFactory 線程工廠
創建新線程並交由線程池管理,默認爲 Executors.defaultThreadFactory()
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
/**
* Executors
* The default thread factory
*/
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-";
}
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); // 5 -> Normal priority for a thread
return t;
}
}
P55_線程池任務丟棄策略分析
- RejectedExecutionHandler 拒絕策略
無法執行且無法存儲的線程就進入拒絕策略
package java.util.concurrent;
/**
* 無法由 ThreadPoolExecutor 執行的任務的處理程序。
*
* @since 1.5
* @author Doug Lea
*/
public interface RejectedExecutionHandler {
/**
* 當 execute 不能接受某個任務時,可以由 ThreadPoolExecutor 調用的方法。
* 因爲超出其界限而沒有更多可用的線程或隊列槽時,或者關閉 Executor 時就可能發生這種情況。
*
* 在沒有其他替代方法的情況下,該方法可能拋出未經檢查的 RejectedExecutionException,
* 而該異常將傳播到 execute 的調用者。
*
* @param r 所請求執行的可運行任務。
* @param executor 試圖執行此任務的執行程序。
* @throws RejectedExecutionException 如果沒有補救方法。
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
/**
* 默認拒絕策略
*/
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
ThreadPoolExecutor 提供的拒絕策略
/**
* 不使用線程池中的線程執行,而是在當前線程中直接執行
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* 拋出異常
*/
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
/**
* 總是拋出 RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException is RuntimeException
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* 將任務直接丟棄,什麼也不做
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/**
* 將阻塞隊列中的隊首的任務丟棄,將當前任務執行 execute 方法
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
P56_線程池拒絕策略實例分析與偏向鎖的關閉
P57_線程池創建線程與執行任務的核心邏輯分析
P58_線程池狀態分析與源碼解讀
線程池中有這樣兩個狀態屬性:線程池狀態、線程池中線程數量;
線程池設計者使用一個字段 ctl 保存這兩個狀態屬性
/**
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* In order to pack them into one int, we limit workerCount to
* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
* billion) otherwise representable. If this is ever an issue in
* the future, the variable can be changed to be an AtomicLong,
* and the shift/mask constants below adjusted. But until the need
* arises, this code is a bit faster and simpler using an int.
*
* The workerCount is the number of workers that have been
* permitted to start and not permitted to stop. The value may be
* transiently different from the actual number of live threads,
* for example when a ThreadFactory fails to create a thread when
* asked, and when exiting threads are still performing
* bookkeeping before terminating. The user-visible pool size is
* reported as the current size of the workers set.
*
* 線程池狀態:
*
* RUNNING: 接受新任務並處理排隊的任務
* SHUTDOWN: 不接受新任務,但處理排隊的任務
* STOP: 不接受新任務,不處理排隊的任務,中斷正在進行的任務
* TIDYING: 所有任務都已終止,workerCount 爲零,轉換爲狀態整理的線程將運行 terminated() 鉤子方法
* TERMINATED: terminated() 方法執行完成
*
* 爲了進行有序比較,這些值之間的數字順序很重要。運行狀態隨時間單調地增加,但不必觸及每個狀態。過渡包括:
*
* RUNNING -> SHUTDOWN
* 調用 shutdown(), 可能隱含在 finalize() 中
* (RUNNING or SHUTDOWN) -> STOP
* 調用 shutdownNow()
* SHUTDOWN -> TIDYING
* 當隊列和線程池數量都爲空時
* STOP -> TIDYING
* 當線程池數量爲空時
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
*
* 在 awaitermination() 中等待的線程將在狀態達到 TERMINATED 時返回。
*
* Detecting the transition from SHUTDOWN to TIDYING is less
* straightforward than you'd like because the queue may become
* empty after non-empty and vice versa during SHUTDOWN state, but
* we can only terminate if, after seeing that it is empty, we see
* that workerCount is 0 (which sometimes entails a recheck -- see
* below).
*/
// 初始值 : 11100000 00000000 00000000 00000000
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// Integer.SIZE = 32
// COUNT_BITS = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 1 << COUNT_BITS --> 00100000 00000000 00000000 00000000
// after -1 --> 00011111 11111111 11111111 11111111
// 即 int 後 29 位用於存儲線程池數量
// 即線程池最大數量爲 2^29-1 ==> 5.36億
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
// int 前 3 位用於存儲 線程池數量
// -1 ==> 11111111 11111111 11111111 11111111
// 0 右移 29 位 = 11100000 00000000 00000000 00000000
private static final int RUNNING = -1 << COUNT_BITS;
// 0 右移 29 位 = 00000000 00000000 00000000 00000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 1 右移 29 位 = 00100000 00000000 00000000 00000000
private static final int STOP = 1 << COUNT_BITS;
// 2 ==> 10
// 2 右移 29 位 = 01000000 00000000 00000000 00000000
private static final int TIDYING = 2 << COUNT_BITS;
// 3 ==> 011
// 3 右移 29 位 = 01100000 00000000 00000000 00000000
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
// 線程池狀態
// c & 11100000 00000000 00000000 00000000
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 線程池數量
// c & 00011111 11111111 11111111 11111111
private static int workerCountOf(int c) { return c & CAPACITY; }
// 邏輯或
private static int ctlOf(int rs, int wc) { return rs | wc; }
P59_線程池狀態遷移與線程創建邏輯源碼分析
ThreadPoolExecutor 源碼
/**
* 在將來某個時間執行給定任務。可以在新線程中或者在現有池線程中執行該任務。
*
* 如果無法將任務提交執行,或者因爲此執行程序已關閉,或者因爲已達到其容量,
* 則該任務由當前 RejectedExecutionHandler 處理。
*
* @param command 要執行的任務。
* @throws RejectedExecutionException 如果無法接收要執行的任務,
* 則由 RejectedExecutionHandler 決定是否拋出 RejectedExecutionException
* @throws NullPointerException 如果 command 爲 null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
// 當前線程池數量 < corePoolSize
if (workerCountOf(c) < corePoolSize) {
// 添加一個 Worker 線程執行任務
if (addWorker(command, true))
return;
// 執行任務失敗(多線程提交任務),重新獲取 ctl
c = ctl.get();
}
// 線程池狀態爲 RUNNING
// 任務放入任務隊列成功
if (isRunning(c) && workQueue.offer(command)) {
// 重新獲取 ctl
int recheck = ctl.get();
// 如果線程池狀態不是運行狀態,則將 command 從任務隊列中移除(回滾)
if (! isRunning(recheck) && remove(command))
// 並將本任務進入拒絕策略
reject(command);
// 線程池數量 = 0 ???
else if (workerCountOf(recheck) == 0)
//
addWorker(null, false);
}
// 線程池
else if (!addWorker(command, false))
reject(command);
}
P60_線程池線程創建與添加邏輯源碼解析
/**
* Checks if a new worker can be added with respect to current
* pool state and the given bound (either core or maximum). If so,
* the worker count is adjusted accordingly, and, if possible, a
* new worker is created and started, running firstTask as its
* first task. This method returns false if the pool is stopped or
* eligible to shut down. It also returns false if the thread
* factory fails to create a thread when asked. If the thread
* creation fails, either due to the thread factory returning
* null, or due to an exception (typically OutOfMemoryError in
* Thread.start()), we roll back cleanly.
*
* @param firstTask the task the new thread should run first (or
* null if none). Workers are created with an initial first task
* (in method execute()) to bypass queuing when there are fewer
* than corePoolSize threads (in which case we always start one),
* or when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core if true use corePoolSize as bound, else
* maximumPoolSize. (A boolean indicator is used here rather than a
* value to ensure reads of fresh values after checking other pool
* state).
* @return true if successful
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
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;
// 線程池數量 +1
if (compareAndIncrementWorkerCount(c))
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 {
// Worker 任務
w = new Worker(firstTask);
final Thread t = w.thread;
// t != null 表示創建線程成功
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();
// Set containing all worker threads in pool. Accessed only when holding mainLock.
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// Worker 是否添加到 workers 裏
if (workerAdded) {
// 啓動線程
t.start();
workerStarted = true;
}
}
} finally {
// 線程未啓動
if (! workerStarted)
// 回滾
addWorkerFailed(w);
}
return workerStarted;
}
/**
* Rolls back the worker thread creation.
* - removes worker from workers, if present
* - decrements worker count
* - rechecks for termination, in case the existence of this
* worker was holding up termination
*/
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
/**
* Class Worker mainly maintains interrupt control state for
* threads running tasks, along with other minor bookkeeping.
* This class opportunistically extends AbstractQueuedSynchronizer
* to simplify acquiring and releasing a lock surrounding each
* task execution. This protects against interrupts that are
* intended to wake up a worker thread waiting for a task from
* instead interrupting a task being run. We implement a simple
* non-reentrant mutual exclusion lock rather than use
* ReentrantLock because we do not want worker tasks to be able to
* reacquire the lock when they invoke pool control methods like
* setCorePoolSize. Additionally, to suppress interrupts until
* the thread actually starts running tasks, we initialize lock
* state to a negative value, and clear it upon start (in
* runWorker).
*/
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}