1. Introduction
It is an important class in the thread pool. It is associated with the Executor and can be said to replace the Executor. Used to create a thread pool to handle high concurrency.
2. Principle
In fact, the implementation principle of java thread pool is very simple. To put it bluntly, it is a thread set workerSet and a blocking queue workQueue. When a user submits a task (that is, a thread) to the thread pool, the thread pool will first put the task into the workQueue. The threads in the workerSet will continuously obtain threads from the workQueue and then execute. When there are no tasks in the workQueue, the worker will block until there are tasks in the queue, and then take them out to continue execution.
After a task is submitted to the thread pool:
- The thread pool first determines whether the number of threads currently running is less than corePoolSize. If so, create a new worker thread to execute the task. If they are all performing tasks, enter 2
- Judge whether the BlockingQueue is full. If not, put the thread into the BlockingQueue. Otherwise, enter 3
- If creating a new worker thread will make the number of currently running threads exceed maximumPoolSize, it will be handed over to RejectedExecutionHandler to process the task.
When ThreadPoolExecutor creates a new thread, it updates the status ctl of the thread pool through CAS
This logic can be seen in the execute method in the source code
The specific logic of this class is all in the source code. You can read it patiently, which will be helpful to you
3. Source code
Important parameters
//ctl is an important variable used to store the number of currently running workers and the status of thread pool. Int is 32 bits. Here, the upper 3 bits of int are used as the flag bit of thread pool status, and the last 29 bits are used as the number of currently running workers private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1; //The following one is the value of the runstate variable, which indicates the running state of the thread 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; //Blocking queue private final BlockingQueue<Runnable> workQueue; //Define a ReentrantLock object to re-enter the lock private final ReentrantLock mainLock = new ReentrantLock(); //Worker collection, which represents the running thread collection private final HashSet<Worker> workers = new HashSet<Worker>(); //Inherited AQS, the condition queue inside private final Condition termination = mainLock.newCondition(); //Maximum number of threads stored in the pool private int largestPoolSize; //Number of tasks completed private long completedTaskCount; //Thread factory, used to create threads private volatile ThreadFactory threadFactory; //The processor can customize the processing when the pool thread is saturated and a new task comes in. The default is to throw an exception private volatile RejectedExecutionHandler handler; //Thread lifetime private volatile long keepAliveTime; //Core thread timeout continue available private volatile boolean allowCoreThreadTimeOut; //Number of core threads private volatile int corePoolSize; //Maximum threads in pool private volatile int maximumPoolSize; //The default processor throws an exception directly private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
CAS method
private boolean compareAndIncrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect + 1); } private boolean compareAndDecrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect - 1); } private void decrementWorkerCount() { do {} while (! compareAndDecrementWorkerCount(ctl.get())); }
It can be seen that ctl variables are operated through CAS
One of the construction methods
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; }
There's nothing to say
Inner class Worker
private final class Worker extends AbstractQueuedSynchronizer implements Runnable { private static final long serialVersionUID = 6138294804551838833L; final Thread thread; Runnable firstTask; volatile long completedTasks; //volatile is used to achieve visibility Worker(Runnable firstTask) { setState(-1); this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); //It can be seen from here that the Worker class implements the Runnable interface. When creating a new thread, the Worker object can be passed as a parameter, which can be regarded as the Runnable interface (a task) } public void run() { runWorker(this); //Run Worker object } //Is the inherited AQS synchronizer an exclusive lock protected boolean isHeldExclusively() { return getState() != 0; } //Inherited AQS synchronizer, trying to acquire lock protected boolean tryAcquire(int unused) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } //Inherited AQS synchronizer, trying to release the lock 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) { } } } }
It inherits AQS and implements the Runnable interface. It can deduce that the Worker object can be used as a task to transfer parameters.
Common method
//This method is to set the thread to its desired state targetState private void advanceRunState(int targetState) { for (;;) { //Infinite loop int c = ctl.get(); //Get the current status value if (runStateAtLeast(c, targetState) || ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) //CAS break; } } final void tryTerminate() { for (;;) { int c = ctl.get(); if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) return; if (workerCountOf(c) != 0) { // Eligible to terminate interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; //Get reentry lock mainLock.lock(); //Lock try { if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { //Set the status to TIDYING try { terminated(); //termination } finally { ctl.set(ctlOf(TERMINATED, 0)); //Set the status to TERMINATED termination.signalAll(); //Wake up the thread in the blocking queue } return; } } finally { mainLock.unlock(); //Release lock } } } public void shutdown() { final ReentrantLock mainLock = this.mainLock; //Get reentry lock mainLock.lock(); //Lock try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); } public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); interruptWorkers(); tasks = drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks; }
Core method
//This method is used to interrupt all starting workers private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) w.interruptIfStarted(); } finally { mainLock.unlock(); } } //Interrupt all idle workers private void interruptIdleWorkers(boolean onlyOne) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); //Lock try { for (Worker w : workers) { Thread t = w.thread; if (!t.isInterrupted() && w.tryLock()) { try { t.interrupt(); //interrupt } catch (SecurityException ignore) { } finally { w.unlock(); /Release lock } } if (onlyOne) break; } } finally { mainLock.unlock(); } } //Reject operation when thread pool is saturated final void reject(Runnable command) { handler.rejectedExecution(command, this); } //Add worker threads to the pool. This core indicates whether the number of threads is the core thread or the maximum thread. This method is to add and execute workers private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { //Infinite loop int c = ctl.get(); //Get ctl int rs = runStateOf(c); //Get the running status value from ctl //Check whether the blocking queue is empty if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { //Infinite loop int wc = workerCountOf(c); //Get the number of worker s from ctl //Judge whether the work quantity exceeds the allowable capacity if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; //Increase workerCount by one with CAS if (compareAndIncrementWorkerCount(c)) break retry; //Judge the running state c = ctl.get(); if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } //These are two switch properties boolean workerStarted = false; //Does the worker start boolean workerAdded = false; //Whether the worker was successfully added Worker w = null; try { w = new Worker(firstTask); //Initialize worker object final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; //Gain reentry lock mainLock.lock(); //Lock try { int rs = runStateOf(ctl.get()); //Get running status if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); //Add the worker object to the workerSet collection int s = workers.size(); //Get the size of the workerSet collection if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; //Set the switch property to true, indicating that the addition is successful } } finally { mainLock.unlock(); //Unlock } if (workerAdded) { t.start(); //Start executing worker workerStarted = true; //Set the switch property to true to start execution } } } finally { if (! workerStarted) //Determine whether the worker has actually started execution addWorkerFailed(w); //If not, call the addWorkerFailed method } return workerStarted; //Returns the Boolean value of whether to start execution } //This method is executed after adding a worker fails private void addWorkerFailed(Worker w) { final ReentrantLock mainLock = this.mainLock; //Reentry lock mainLock.lock(); //Lock try { if (w != null) workers.remove(w); //Remove this worker from the workerSet decrementWorkerCount(); //Subtract one from workercount, CAS operation tryTerminate(); //Terminate current worker } finally { mainLock.unlock(); //Unlock } } //This method is used to handle the worker exit logic private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; //Reentry lock mainLock.lock(); //Lock try { completedTaskCount += w.completedTasks; //Add the number of tasks completed by this worker to the completedTaskCount workers.remove(w); //Quit again } finally { mainLock.unlock(); //Unlock } tryTerminate(); //Then terminate the worker int c = ctl.get(); //Get status value 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); } } //This method is used to get the task private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { //Infinite loop int c = ctl.get(); int rs = runStateOf(c); //Get running status //Check whether the blocking queue is empty if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); //Get number of worker threads // Check again for timeout boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; //Determine whether the number of worker threads overflows and recheck timeout if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { //Check the timeout again. If there is no timeout, get a task from workQueue Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; //Return to this task timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } } //Run worker final void runWorker(Worker w) { Thread wt = Thread.currentThread(); //Get current thread Runnable task = w.firstTask; //Get the task w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); //Lock if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); //Operations before execution, life cycle hook Throwable thrown = null; try { task.run(); //Run task } 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); //Operation after execution, life cycle hook } } finally { task = null; //After the task is executed, it is set to null for the convenience of GC w.completedTasks++; //Number of tasks completed at the same time plus one w.unlock(); //Release lock } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); //Finally, execute the exit worker operation } } //The overall logic of addWorker called in execution is to judge the relationship between the number of threads and the number of core threads and the maximum number of threads, and determine how the worker should be created or reject ed through this relationship public void execute(Runnable command) { if (command == null) throw new NullPointerException(); 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); }
3. Summary
1. ThreadPoolExecutor is used to perform logical processing of tasks accepted by the thread pool and thread execution. If you want to compare with Executor, Executor is a large range. It does not consider the processing logic inside the thread pool, but can implement business logic and create a thread pool that conforms to business logic; The ThreadPoolExecutor is a small scope, which can be said to be the internal implementation of the Executor. It focuses on the code logic within the thread pool, thread scheduling and task scheduling.
2. ThreadPoolExecutor can also be used in multi-threaded environment. Its method uses ReentrantLock to add and release locks. At the same time, the ctl attribute uses CAS atomic operation to ensure data consistency; In addition, volatile keyword is used to modify attributes in a few places to ensure the visibility of attributes.
3. The method in ThreadPoolExecutor basically judges the condition based on the attribute ctl. The upper three bits of ctl are the saved RunState, and the lower 29 bits are the number of threads in the thread pool