Foreword (must see here!!)
I'm sure you know the process of thread pool execution, but how do you configure thread size, how do you really use it, and how do you configure it reasonably with quantified metrics?This article will analyze the implementation of thread pools from the source code point of view and show how thread pools are quantified.
At first glance, is it frightened by the size, don't be afraid, I am not afraid to insist when I write. You can see that it is also possible. All the source code, basically every line has been commented, unless it is very simple, you can't look at it and also collect it. I believe you will use it in the future.
This article is not only for sharing, but also for supporting you to read and learn as a dictionary after you have listened to it.
Be aware of interrupts, UnSafe, BlockingQueue notification mechanisms, AQS (status, queue, CAS) before reading this article.
Task Submission
Thread pool submission process review
On the left is the inheritance relationship of the class.
On the right is the class method level, method submission execution flow.
Thread pool state transition
Executor
Is an interface written by doug lea that specifically submits tasks
execute: Submit the task.
ExecutorService
Executor interface declarations have too few capabilities, so the interface ExecutorService interface inherits Executor and provides more methods, requiring the ThreadPoolExecutor implementation. Here is a brief description of the functions of each method.
shutdown: Closes the thread pool, no longer accepts new tasks, and the submitted tasks continue to execute.
shutdownNow: Closes the thread pool, no longer accepts new tasks, and the executing task attempts to terminate.
isShutdown: Verify that the thread pool is closed.
isTerminated: Verify that the thread pool is closed after shutdown or shutdownNow is executed.
awaitTermination: Wait for a period of time and return false if it is time out and no terminated, otherwise the thread pool has terminated and returns true.
submit: submit the task.
invokeAll: Performs all tasks with a return value of List<Future<T>.
invokeAny: Executes N tasks, returns when one execution is complete, with a return value of T (the specific result).
AbstractExecutorService
AbstractExecutorService implements ExecutorService, providing implementations of some common methods for thread pools.
invokeAll and invokeAny
The two methods are less used and the source code is simple.invokeAny mainly uses a queue to get the result of the first execution.
CompletableFuture is recommended if asynchronous computing tasks are involved.
submit
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); }
NewTaskForis actually an instance of FutureTask, which implements RunnableFuture, which inherits Runnableand Future.
ThreadPoolExecutor
Really enter the thread pool source learning phase.
Member variable description
// The ctl stores the thread pool state and number of threads. The integer has 32 bits. The first three bits represent the thread pool state, and the last 29 bits represent the number of thread pools. // Initialized with RUNNING status and 0 starting threads. private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // This represents the initial value of 29 bits (Integer.SIZE=32) private static final int COUNT_BITS = Integer.SIZE - 3; // Maximum number of supported threads 2^29-1 private static final int CAPACITY = (1 << COUNT_BITS) - 1; // The following are the four states of the thread pool, represented by the first three of 32 bits // 111 00000000000000000000000000000 private static final int RUNNING= -1 << COUNT_BITS; // 000,000,000,000,000,000,000,000,000,000 reject new task submissions, completing the tasks in the queue and continuing the tasks in progress. private static final int SHUTDOWN =0 << COUNT_BITS; // 001 00000000000000000000000000000Refuse new task submissions and empty tasks in queue private static final int STOP =1 << COUNT_BITS; // 010 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 00 All tasks were destroyed, workCount=0, when the thread pool filling is converted to TIDYING, the hook method terminated() is executed private static final int TIDYING=2 << COUNT_BITS; // When the 011 00000000000000000000000000000terminated() method completes, the state of the thread pool changes to TERMINATED. private static final int TERMINATED =3 << COUNT_BITS; // Gets the state of the current thread pool (top 3 bits) private static int runStateOf(int c) { return c & ~CAPACITY; } // Gets the number of threads in the current thread pool (last 29 bits) private static int workerCountOf(int c){ return c & CAPACITY; } // Update status and quantity private static int ctlOf(int rs, int wc) { return rs | wc; } // Less than to determine if C is less than S, such as runStateLessThan(var,STOP), then var can only be (RUNNING,SHUTDOWN) private static boolean runStateLessThan(int c, int s) { return c < s; } // Is C >= S private static boolean runStateAtLeast(int c, int s) { return c >= s; } // Determine if the state is RUNNING private static boolean isRunning(int c) { return c < SHUTDOWN; } // The unnable task submitted by the execute() method will be placed in this blocked queue if no thread is currently available to execute the task. private final BlockingQueue<Runnable> workQueue; // This lock is used to protect the workers below. Accessing workers must acquire this lock. private final ReentrantLock mainLock = new ReentrantLock(); // Sets all worker threads in the pool to be accessed only when holding a master lock. - There are two main reasons why thread-safe data structures are not used here: 1. With compound operation, increase worker Also update largestPoolSize. 2. When a thread is interrupted,If not locked,Concurrent interrupt threads may occur,Cause interruption storm. private final HashSet<Worker> workers = new HashSet<Worker>(); // Thread communication means, used to support the awaitTermination method, awaitTermination's role waits for all tasks to complete, and supports setting timeout, the return value indicates whether or not the timeout is reached. private final Condition termination = mainLock.newCondition(); // Record the maximum value since the workers'history and obtain the main lock before each acquisition // Each time a worker is added, it is determined if the current workers.size() is greater than largestPoolSize, and if it is greater, the current maximum is assigned to largestPoolSize. private int largestPoolSize; // Count all completed tasks to obtain the main lock before each acquisition // Each worker has its own member variable completedTasks to record the number of tasks performed by the current worker. The number of completedTasks in the worker is added to the completedTaskCount index only when the current online worker worker thread terminates. private long completedTaskCount; // Thread factory, which is used to construct threads with some business identity or something. private volatile ThreadFactory threadFactory; // Rejection policy. Default four AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy. Recommend that you implement them and increase your monitoring metrics. private volatile RejectedExecutionHandler handler; // When the number of threads in the thread pool exceeds corePoolSize, how long after the idle threads are destroyed. private volatile long keepAliveTime; // Time when the core thread pool is idle to allow destruction. private volatile boolean allowCoreThreadTimeOut; // Thread pool core thread pool size private volatile int corePoolSize; // Maximum number of threads that a thread pool can establish private volatile int maximumPoolSize; // Default rejection policy AbortPolicy private static final RejectedExecutionHandler defaultHandler = new AbortPolicy(); // Security control access (mainly for shutdown and shutdownNow methods) private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread"); // When threadPoolExecutor is initialized, the acc value of the AccessControlContext object is also initialized. // Assignment during initialization of threadPoolExecutor, acc object refers to a snapshot of the current call context, including the AccelessControlContext inherited by the current thread and any limited privilege range, so that the context can be checked at a later point in time, possibly in another thread. private final AccessControlContext acc;
We need to make sure that the task is Runnable (or task or command), and the thread is worker.
Submit Task Main Process Analysis
AbstractExecutorService.submit()
public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); // Construct a FutureTask instance as long as the purpose is to get the return value. This section is ignored first, and the submission process RunnableFuture<T> ftask = newTaskFor(task); //Execute Tasks execute(ftask); return ftask; }
ThreadPoolExecutor.execute()
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); // Gets the current thread pool state and number of threads container if (workerCountOf(c) < corePoolSize) {// Determine if the number of threads working online is less than the number of core threads configured if (addWorker(command, true))// Increase worker if less than core threads return; // Increase worker success and return when worker runs c = ctl.get();// Thread pool rejected adding worker and retrieved thread pool state } // The worker increase failed or the number of threads in the current thread pool exceeded the number of core threads. if (isRunning(c) && workQueue.offer(command)) {// Determines if the current thread pool state is RUNNING or if the task is joined to a blocked queue, the offer is not blocked. int recheck = ctl.get();// Get the current thread pool state if (! isRunning(recheck) && remove(command))// Determine if the current thread pool state is RUNNING or not, instead of deleting command tasks from workQueue reject(command);//Execute Rejection Policy else if (workerCountOf(recheck) == 0)//View the number of current worker threads addWorker(null, false);//If the current number of threads is 0, the task you just started must be in the blocked queue, at which point a thread without tasks starts running. } // When the current state of the thread pool is not RUNNING or adding worker to workQueue fails else if (!addWorker(command, false))//At this point, the queue is full, or the status is not RUNNING, try opening a worker again reject(command); //Failed to add worker and execute rejection policy. }
ThreadPoolExecutor.addWorker(Runnable firstTask, boolean core)
//Create a new thread to perform the current task //firstTask: Specifies the first task performed by the new thread or does not perform the task private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get();// Container to get current thread pool state and number of threads int rs = runStateOf(c);// Get the current running state // Check if queue empty only if necessary. // Commit is not allowed if the thread pool state is SHUTDOWN, STOP, TIDYING, TERMINATED. // &Later special cases where the state of the thread pool is SHUTDOWN and the task to be executed is Null and the queue is not empty allow an additional thread to help the task in the queue run through, because the shutdown state allows the task in the blocked queue to be executed if (rs >= SHUTDOWN &&! (rs == SHUTDOWN && firstTask == null &&! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c);// Current Threads if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))// Whether or not you exceed the maximum value of the current contract, refuse to join if you exceed it, and return directly, depends on what the core is passing, that is, what the current situation is, whether the core thread pool is exhausted or what happens. return false; if (compareAndIncrementWorkerCount(c))//If the contract value is not exceeded, the number of worker s will be increased by CAS, and success will jump out of the outer loop. break retry; c = ctl.get(); // Get the current thread pool state + number of threads container again if (runStateOf(c) != rs)// Determine if the current running state has changed continue retry; //Outer loop reexecute // RunStateOf(c)!= RS This judgment mainly looks at the state of the current thread pool changing. // -Changed, re-execute from the outer loop, re-check the status. // -unchanged, re-execute from current loop, re-execute CAS operation. // else CAS failed due to workerCount change; retry inner loop } } boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask);//Build a worker and assign the current task to the current worker. Look at the source code for the new Worker here, and you'll find that a new thread is directly given to the current worker object //Worker(Runnable firstTask) { // setState(-1); // runWorker does not allow interruption until running // this.firstTask = firstTask; // this.thread = getThreadFactory().newThread(this); be sure to focus on this // } final Thread t = w.thread; //Get the thread of the current worker if (t != null) { final ReentrantLock mainLock = this.mainLock;//Acquire Main Lock mainLock.lock();//Locking try { int rs = runStateOf(ctl.get());//Get the current running state if (rs < SHUTDOWN ||//If the current state is <SHUTDOWN, then RUNNABLE (rs == SHUTDOWN && firstTask == null)) {//Or the status is SHUTDOWN and the current task is empty, which is simply to create a new worker (thread), such as the scene mentioned above, which is still in the blocking queue, but is no longer allowed to submit. if (t.isAlive()) // Check if the current thread has started running throw new IllegalThreadStateException();//How did the thread just new throw away? This throws an exception. The wrong thread state is abnormal. workers.add(w);// Increase wokrer, which is why it is locked above. For hashSet, see the description of member variable wokers above. int s = workers.size();//Get the total number of current worker s if (s > largestPoolSize)//Total number of current worker s if greater than previously recorded thread pool size largestPoolSize = s;//Thread pool size reassignment workerAdded = true;//Indicates that this worker addition was successful } } finally { mainLock.unlock();// Release main lock } if (workerAdded) {// If worker addition succeeds t.start();//Thread runs workerStarted = true;//Indicates that the thread has run } } } finally { if (! workerStarted)//Determine if the worker thread is running properly addWorkerFailed(w);//Failure to run indicates failure to add worker } return workerStarted;//The return is whether the current worker is running. }
When t.start() is called, the run method in the Worker will be executed. Why? Look at the Worker object to construct this block. 3) This step directly crams the Worker in, so you can see.
Worker(Runnable firstTask) { setState(-1); // runWorker does not allow interruptions until it runs this.firstTask = firstTask; ===> 3) this.thread = getThreadFactory().newThread(this);// Be sure to focus on this }
Worker
Take a closer look at the Worker class, which inherits AQS and implements Runnable.
Reasons for inheriting AQS: Threads have only two states, one is exclusive to indicate that the thread is running, the other is unlocked idle, distinguished by AQS state. There is no need for ReentrantLock here, mainly because ReentrantLock allows reentry.
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
Woker.run()
/** Delegates main run loop to outer runWorker */ public void run() { runWorker(this); }
runWorker(this)
To illustrate here, the runWorker method is a method that executes getTask iteratively whenever a thread is opened. The getTask method may be blocked and will only close the current task if task==null &&getTask==null.
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); //Get Current Thread Runnable task = w.firstTask; //Get the current thread task w.firstTask = null; //Assign the task to it when you get it, and then set firstTask to null. w.unlock(); //Settings allow interruptions, which correspond to setState (-1) where Worker constructs boolean completedAbruptly = true;//Identify if the task is completed immediately. try { while (task != null || (task = getTask()) != null) {//Get Tasks alone w.lock();//Attempt to lock //If (thread pool state >=STOP or (thread interrupted and thread state >=STOP)) and the current thread is not interrupted. // Two cases: //1) If the current thread pool state is >=Stop and the current thread is not interrupted, then an interrupt is executed. //2) Or if the current thread is in the interrupted state and the state of the thread pool is >=Stop (note that Thread.interrupted erases the interrupt identifier), then because the interrupt identifier has been erased, wt.isInterrupted() must return true, at which point the current thread will still be interrupted.The second run StateAtLeast (ctl.get(), STOP) is equivalent to a second check. if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt();//Interrupt current thread try { beforeExecute(wt, task);//Pre-operation, empty method, can be implemented by the business itself Throwable thrown = null; try { task.run();//Execute the task, which actually calls the run method of the ftask passed in } 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);//Post-operation, empty method, can be implemented by the business itself } } finally { task = null;//Finally set task to null w.completedTasks++;//Completed Tasks Counter+1 w.unlock();//Release the exclusive lock on the current thread } } completedAbruptly = false;//When a task is completed, it is marked by the above processes that the task is not completed immediately. } finally { // Be careful.Executing here means task == null and getTask() returns null.Indicates that there are not so many threads in the current thread pool to perform tasks. You can either kill more than corePoolSize worker threads, or interrupt them, such as SHUTDOWN, and cause getTask to return Null processWorkerExit(w, completedAbruptly);//Task Exit Process, Separate Analysis } }
getTask
How to get the task
Note that where the getTask method is called is a while dead loop and will not exit as long as the getTask has a return value.
Exiting the loop means that the part of the thread that exceeds the number of core threads has been destroyed.
private Runnable getTask() { boolean timedOut = false; //Is the final acquisition task timed out for (;;) {// Dead cycle int c = ctl.get();// Get Containers int rs = runStateOf(c);//Get the current running state // If the current state is >=SHOTDOWN state && (running state is STOP or queue is empty). // 1) If the state of the thread pool is >=STOP, no more tasks in the queue will be processed at this time, and the number of worker records will be reduced, and the returned tasks will be null, at which point the processWorkerExit will execute the worker exit operation in the runRWorker method. // 2) If the state of the thread pool is >=SHUTDOWN and the workQueue is empty, it means that the thread pool is above SHOTdown and there are no tasks waiting, then the task cannot be acquired and getTask returns null. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount();//Reduce the count of threads in the thread pool and exit threads in finally in runWorker return null;//Return null } int wc = workerCountOf(c);//Get the current number of wokrer s // Whether to turn on timeout mechanism. // If the number of core threads allows timeouts, timed is true, turning on the timeout mechanism. // If the number of core threads does not allow timeouts, it depends on whether the current number of bus threads is > the number of core threads. If it is greater than this, the timed is true and the current woker turns on the timeout mechanism. boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // (If the current thread pool size exceeds the maximum setting or if the thread settings allow timeouts and the current woker gets the task timeout) and if the current thread pool size is not zero or the blocking queue is empty, this returns null and reduces the thread pool thread count. if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { // To meet these criteria, reduce the worker count. Why not use decrementWorkerCount here? // The decrementWorkerCount() is used above because it is determined that in any case, the number will be reduced and that one more reduction will be okay, since the thread pool is shut down to release resources. if (compareAndDecrementWorkerCount(c))//Unlike this, the state of the thread pool may be RUNNING, subtracting it once more may result in the task not getting the worker to run. return null; continue;//false jumped out of this cycle and checked again. } try { //Determine whether timeouts are allowed, allow timeouts to be set with poll, and use take-dependent timeout mechanisms instead Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r;//Task returns if it does not equal null timedOut = true;//This must have been caused by a timeout, so timedOut is set to true } catch (InterruptedException retry) {//Because blocking queue poll take s are all locked with fast response interrupts (lockInterruptibly()), interrupt exceptions need to be caught. This method is judged by Thread.interrupted(), which has the characteristic of erasing the interrupt state, indicating that the getTask method does not respond to interrupts. timedOut = false; } } }
processWorkerExit
If a task exits abnormally, it returns with a worker and the current task is lost.
The task did not exit abnormally:
1) How core threads allow timeouts, and when there are tasks in the task queue, you must ensure that there is a worker in the thread pool that does not execute addWorker in this method.
2) If the core thread does not allow timeouts, ensure that the number of threads in the current thread pool >=the number of core threads. If the number of threads in the current thread pool is less than the number of core threads, still add a worker and execute addWorker.
private void processWorkerExit(Worker w, boolean completedAbruptly) { //If the task is completed suddenly, the number of worker threads will be -1 //If completedAbruptly is true, an exception occurred during thread execution and the number of workerCount s needs to be reduced by one //If completedAbruptly is false, the workerCount has been subtracted by one in the getTask method, and no further subtraction is required here if (completedAbruptly) decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //Update statistics for number of completed tasks completedTaskCount += w.completedTasks; //Remove tasks from worker workers.remove(w); } finally { mainLock.unlock(); } //An attempt was made to close the thread pool, but it would not close if it was functioning properly. This critical condition for closing is discussed separately in Analysis tryTerminate. tryTerminate(); int c = ctl.get(); //This place is more winding, so look good. // completedAbruptly=true stands for the abnormal end. Whatever can you look at the code in runWorker? completedAbruptly=false if there are no exceptions. //If the current state of the thread pool is either SHUGTDOWN or RUNNING, then the thread has stopped and nothing will be done. //If the task ends abnormally, add a worker //Note: Don't ask me why I want to delete a worker on top of that, but add it. Make it clear that the task has quit the dead cycle of getTask and will never go back. Only a new worker will be added. if (runStateLessThan(c, STOP)) { if (!completedAbruptly) { //Go here and say it's not an abnormal exit int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && !workQueue.isEmpty())//If the core thread is allowed to time out and there are tasks in the current queue that are not running, then you must leave one thread behind and not die altogether. min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false); } }
tryTerminate
interruptIdleWorkers(ONLY_ONE); curious about why only one worker is interrupted here, it involves the elegant exit of the thread pool.
When executing to ==> (1), the thread pool can only be in two states:
1) STOP status, workQueue may be valuable at this time, workQueue is in the process of emptying.
2) SHUTDOWN status and workQueue is empty.
Both states indicate that the thread pool is about to close, or that idle threads are no longer used at this time. Close one at this time, anyway, early or late.
//Determine whether to end thread pool based on thread pool state final void tryTerminate() { for (; ; ) { int c = ctl.get(); //RUNNING state, thread pool cannot be terminated //A thread pool state of TIDYING or TERMINATED indicates that the thread pool is on the way to termination and no longer needs to be terminated. //The state is SHUTDOWN, but the task queue is not empty and the thread pool cannot be terminated if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && !workQueue.isEmpty())) return; //Number of worker threads is not equal to 0, interrupt an idle worker thread and return ===>(1 //Thread pool must be STOP state or SHUTDOW side-by-side queue is not empty at this time, either trying to plant so try to interrupt an idle worker at this time if (workerCountOf(c) != 0) { interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Set thread pool state to TIDYING, call terminated method if set successfully if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { try { //Subclass implementation terminated(); } finally { // Set state to TERMINATED ctl.set(ctlOf(TERMINATED, 0)); termination.signalAll(); } return; } } finally { mainLock.unlock(); } } }
interruptIdleWorker
//Interrupt idle threads, because idle threads must be kept waiting through LockSupport.park. If the interrupt command is executed, the currently idle threads will immediately throw an interrupt exception and wake up. private void interruptIdleWorkers(boolean onlyOne) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //Traverse the worker and, according to onlyOne, interrupt only one idle thread for ture for (Worker w : workers) { Thread t = w.thread; //Thread is not interrupted and the thread is idle tryLock() determines if it is idle if (!t.isInterrupted() && w.tryLock()) { try { t.interrupt(); } catch (SecurityException ignore) { } finally { w.unlock(); } } if (onlyOne) break; } } finally { mainLock.unlock(); } }
Indicator Acquisition
threadPoolExecutor.getActiveCount(): Returns the approximate number of threads that are executing the task, note that this is not an exact value, so for now the role is only for display, not as a critical condition.
public int getActiveCount() { final ReentrantLock mainLock = this.mainLock; mainLock.lock();//Prevent new worker s from joining and executing try { int n = 0; for (Worker w : workers)//During the current cycle, the worker's status is also continuous, for example, it appears that he has just finished the statistics, but before the statistics are finished, he runs through, which is wrong. if (w.isLocked()) ++n; return n; } finally { mainLock.unlock(); } }
Real-time metrics acquisition
threadPoolExecutor.getCompletedTaskCount(): Returns the approximate total number of tasks that have been completed. Why not be precise for reasons similar to getActiveCount that are not analyzed.
threadPoolExecutor.getTaskCount(): Returns the approximate total number of tasks scheduled for execution. Why is this inaccurate? Similar to getActiveCount, it is not analyzed.
threadPoolExecutor.getLargestPoolSize(): Returns the maximum number of threads historically created in the pool.
threadPoolExecutor.getPoolSize(): Returns the current number of threads in the pool.
Fixed Indicator Acquisition
Specified when a thread pool is constructed
threadPoolExecutor.getRejectedExecutionHandler(): Returns the current handler for an unexecutable task
threadPoolExecutor.getThreadFactory(): Returns the thread factory used to create a new thread
threadPoolExecutor.getCorePoolSize(): Returns the number of core threads
threadPoolExecutor.getKeepAliveTime(): Returns the thread keep alive time
threadPoolExecutor.getMaximumPoolSize(): Returns the maximum number of threads allowed
threadPoolExecutor.getQueue(): Returns the task queue used by this executor
Rejection Policy
AbortPolicy,CallerRunsPolicy,DiscardPolicy,DiscardOldestPolicy
Rejection Policy Recommends Self-Implementation
The rejection strategy suggests its own implementation, which, together with real-time statistical indicators, facilitates the analysis of thread pool capacity and the appropriateness of its use, is really important in practice.
public class MonitoredCallerRunsPolicy implements RejectedExecutionHandler { private final static Logger LOG = LoggerFactory.getLogger(MonitoredCallerRunsPolicy.class); private final String threadPoolName; public final static String THREAD_CALLER_RUN = "ThreadPoolCallerRun"; public MonitoredCallerRunsPolicy(String threadPoolName) { this.threadPoolName = threadPoolName; } @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor threadPoolExecutor) { if (!threadPoolExecutor.isShutdown()) { r.run(); // todo adds necessary real-time monitoring metrics LOG.info("thread pool:{} trigger caller Run", threadPoolName); Cat.logEvent(THREAD_CALLER_RUN, threadPoolName); } } }
Other method descriptions
shutdown
Close the thread pool, no longer accept new tasks, and the submitted tasks continue to execute.
Note: When the running task is finished and the current workQueue is empty, it exits directly. The thread goes to the end. The specific source code is getTask's
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null;//Returning null executes the logic that processWorkerExit exits. }
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess();//Security Policy Judgment advanceRunState(SHUTDOWN);//RUNNING->SHUTDOWN State Transition interruptIdleWorkers();//Interrupt all idle threads. This method was analyzed above onShutdown(); // Scheduled ThreadPoolExecutor reserved hook } finally { mainLock.unlock(); } tryTerminate();//Try closing idle threads. This method is described above }
shutdownNow
Close the thread pool, no longer accept new tasks, and the task being executed attempts to terminate.
public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess();//Permission Verification advanceRunState(STOP);//Thread pool status set to STOP interruptWorkers();//Forced interruption of all States is either -1 or all started workers. tasks = drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks; }
private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers)//Loop all worker s w.interruptIfStarted();//Direct execution interrupt } finally { mainLock.unlock(); } }
void interruptIfStarted() { Thread t; //Only when a worker has just been built, the state state value is -1 (which also indicates that the newly built worker cannot be interrupted), otherwise it is >=0 if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } }
isShutdown
Verify that the thread pool is closed.Determine if the state is RUNNING.
public boolean isShutdown() { return ! isRunning(ctl.get()); }
isTerminated
Verify that the thread pool is closed after shutdown or shutdownNow is executed.
The source code is simple to see if the current thread pool state is TERMINATED
awaitTermination
Wait for a while, if it is time out, false is returned before terminated, otherwise the thread pool has terminated and true is returned.
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (;;) { if (runStateAtLeast(ctl.get(), TERMINATED))//If the status is already TERMINATED return true; if (nanos <= 0) return false; nanos = termination.awaitNanos(nanos);//Notification is sent here after the thread pool state is set to TERMINATED in the notification mechanism tryTerminate } } finally { mainLock.unlock(); } }
isTerminating
Is it stopping? Look at the source code analysis below and you will see it all at once
public boolean isTerminating() { int c = ctl.get(); return ! isRunning(c) && runStateLessThan(c, TERMINATED);//See if the state TIDYING/STOP/SHUTDOWN returns true if it is in these three states, and the thread pool is stopping, either in these three states or in RUNNING state, then in TERMINATED state, it returns false, and the thread pool is terminated. } private static boolean runStateLessThan(int c, int s) { return c < s; }
prestartCoreThread
Start an idle thread as the core thread,
- If the number of core threads has reached the threshold, failure is added, false is returned, and false is returned if the thread pool is above SHUTDOWN.
- Only if the real thread runs by calling the start method will it return true.
public boolean prestartCoreThread() { return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true); }
prestartAllCoreThreads
Start all core threads so they wait to get the task.
public int prestartAllCoreThreads() { int n = 0; while (addWorker(null, true))//null for idle threads, true for added core threads ++n;//Dead loops only add idle worker s return n; }
FutureTask
introduce
FutureTask is simply a task that can be submitted, used to get results, get the status of execution, or canceled.
Member Variable Gets the offset of the member variable from the object instance
private static final sun.misc.Unsafe UNSAFE; //The offset is obtained to support operations that change the shape of an atom in UnSafe, such as the following headline <CAS Operation> private static final long stateOffset;//state property offset private static final long runnerOffset;//runner attribute offset private static final long waitersOffset;//waiter attribute offset static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> k = FutureTask.class; //The getDeclaredField method gets all the declared fields of a class, that is, public, private, and proteced, but not the declared fields of the parent class //UNSAFE.objectFieldOffset gets the offset of the value field in the object (actually, the offset from a field to the head of the object through which the field can be located quickly). This object actually refers to the object instance, and the reference object instance is in the heap. stateOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("state")); runnerOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("runner")); waitersOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("waiters")); } catch (Exception e) { throw new Error(e); } }
CAS Operation
//this: Current Object Instance //Offset: The offset of the current property from the object header of the this object instance //Current Expected Status: NEW //Value to be updated: COMPLETING UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)
Construction method run method setException method
protected void setException(Throwable t) { //Decrease the status of the current task from new to completing, and successful updates indicate that the task has been executed and results copied if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t;//Set Return Results UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // The reason putIntVolatile is not used here is that the state field of the current task is modified by volatile. finishCompletion();//Task Execution Complete Final End } }
set method
protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v;//Set Return Results UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // Set the final state of the current task to NORMAL finishCompletion();//Task Execution Complete Final End } }
handlePossibleCancellationInterrupt
//This method is very simple, that is, if the state is in interrupt, it will give up CPU time, which is a dead loop, waiting for the final state to become interrupted. private void handlePossibleCancellationInterrupt(int s) { if (s == INTERRUPTING) while (state == INTERRUPTING) Thread.yield(); //Allow CPU time to wait for task to complete }
cancel
public boolean cancel(boolean mayInterruptIfRunning) { if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))//Set task status to interrupted or canceled return false;//If the state is not new or the setting state fails, return false to indicate cancel failed try { // in case call to interrupt throws exception if (mayInterruptIfRunning) { try { Thread t = runner; if (t != null) t.interrupt();//If it is the interrupt mode, call the thread's interrupt method. } finally { // final state UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);//And set the final state of the current task to INTERRUPTED } } } finally { finishCompletion();//Closing up, as described above } return true; }
Not finished yet.Best practices, as well as some core knowledge points, will be completed by Saturday