Objective
This article mainly tells Threadpoolexecutor's source code analysis, runs through the class creation, the task adds to the thread pool closes the entire process, lets you know its why. Hopefully you'll find out how threadpoolexecutor is adding tasks, performing tasks, and extending knowledge points through this blog post. So let's take a look at Threadpoolexecutor's inheritance.
Inheritance relationship
Executor interface
public interface Executor { void execute(Runnable command);}
Executor interface has only one method execute, incoming thread task parameter
Executorservice interface
Public interface Executorservice extends Executor {void shutdown (); List<runnable> Shutdownnow (); Boolean IsShutDown (); Boolean isterminated (); Boolean awaittermination (long timeout, timeunit unit) throws Interruptedexception; <T> future<t> Submit (callable<t> Task); <T> future<t> Submit (Runnable task, T result); Future<?> Submit (Runnable Task); <T> list<future<t>> InvokeAll (collection<? extends callable<t>> tasks) throws Interru Ptedexception; <T> list<future<t>> InvokeAll (collection<? extends callable<t>> tasks, Long timeout, timeunit unit) throws Interruptedexception; <T> T invokeany (collection<? extends callable<t>> tasks) throws Interruptedexception, Executionex ception; <T> T invokeany (collection<? extends callable<t>> tasks, Long timeout, timeunit unit) throws Interruptedexception, Executionexception, TimeoutException;}
The Executorservice interface inherits the executor interface and adds a series of methods such as Submit, shutdown, InvokeAll, and so on.
Abstractexecutorservice Abstract class
Public abstract class Abstractexecutorservice implements Executorservice {protected <T> runnablefuture<t> Newtaskfor (Runnable Runnable, T value) {return new futuretask<t> (Runnable, value); } protected <T> runnablefuture<t> newtaskfor (callable<t> callable) {return new futuretask< T> (callable); Public future<?> Submit (Runnable Task) {if (task = = null) throw new NullPointerException (); runnablefuture<void> ftask = newtaskfor (task, NULL); Execute (ftask); return ftask; } public <T> future<t> submit (Runnable task, T result) {if (task = = null) throw new NULLPOINTEREXCE Ption (); runnablefuture<t> ftask = newtaskfor (task, result); Execute (ftask); return ftask; } public <T> future<t> submit (callable<t> Task) {if (task = = null) throw new NULLPOINTEREXCEP tion (); Runnablefuture<t> Ftask =Newtaskfor (Task); Execute (ftask); return ftask; } Private <T> T doinvokeany (collection<? extends callable<t>> tasks, Boo Lean timed, long Nanos) throws Interruptedexception, Executionexception, timeoutexception {...} Public <T> T Invokeany (collection<? extends callable<t>> tasks) throws Interruptedexception, Exec utionexception {...} Public <T> T Invokeany (collection<? extends callable<t>> tasks, long timeout, Timeunit unit) throws Interruptedexception, Executionexception, timeoutexception {...} Public <T> list<future<t>> InvokeAll (collection<? extends callable<t>> tasks) throws interruptedexception {...} Public <T> list<future<t>> InvokeAll (collection<? extends callable<t>> tasks, Long timeout, timeunit unit) throwS interruptedexception {...}}
The Abstractexecutorservice abstract class implements the Executorservice interface and provides default implementations of some methods, such as the Submit method, the Invokeany method, and the InvokeAll method.
Like the Execute method, the thread pool shutdown method (shutdown, Shutdownnow, and so on) does not provide a default implementation.
Threadpoolexecutor
Let's first introduce the status of the Threadpoolexecutor thread pool.
Thread pool Status
int is 4 bytes, or 32 bits ( 注:一个字节等于8位
)
Record thread pool status and number of threads (total 32 bits, first three bits representing thread pool status, last 29 bits representing number of threads) Private final Atomicinteger ctl = new Atomicinteger (Ctlof (RUNNING, 0));// Number of threads statistic digits integer.size=32 private static final int count_bits = integer.size-3;//capacity 11111111111111111111111111111p rivate static final int capacity = (1 << count_bits)-1;//running 111 00000000000000000000000000000private static fin Al int RUNNING =-1 << count_bits;//close 00000000000000000000000000000private static final int SHUTDOWN = 0 << count_bits;//stop 001 00000000000000000000000000000private static final int stop = 1 << count_bits;//integer Daniel 010 00000000000000000000000000000private static final int tidying = 2 << count_bits;//termination 011 00000000000000000 000000000000private static final int TERMINATED = 3 << count_bits;//Gets the running state (gets the first 3 bits) private static int runstateof (int c) {return C & ~capacity;} Gets the number of threads (gets the post 29 bits) private static int workercountof (int c) {return C & capacity;} private static int Ctlof (int rs, int wc) {return rs | wc;}
- RUNNING: Accepting new tasks and handling tasks in the blocking queue
- SHUTDOWN: Rejecting new tasks but handling tasks in the blocking queue
- STOP: Reject a new task and discard the task in the blocking queue while interrupting the task being processed
- Tidying: All tasks are executed (containing tasks within the blocking queue) The current thread pool is 0, and the terminated method will be called
- TERMINATED: Terminating state. Terminated the state after the method call completes
Thread Pool state transitions
RUNNING -> SHUTDOWN 显式调用shutdown()方法, 或者隐式调用了finalize()方法(RUNNING or SHUTDOWN) -> STOP 显式调用shutdownNow()方法SHUTDOWN -> TIDYING 当线程池和任务队列都为空的时候STOP -> TIDYING 当线程池为空的时候TIDYING -> TERMINATED 当 terminated() hook 方法执行完成时候
constructor function
There are four constructors, and the other three are calling this constructor in the code below
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {}
Parameter introduction
parameter |
type |
meaning |
corepoolsize |
int |
number of core threads |
maximumpoolsize |
int |
Maximum number of threads |
keepalivetime |
long |
survival time |
unit |
timeunit |
time Unit |
workqueue |
blockingqueue |
hold thread's queue |
threadfactory |
threadfactory |
Create a thread factory |
handler |
rejectedexecutionhandler |
extra thread processor (Deny policy) |
Submit Task Submission
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask;}public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask;}public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask;}
The process steps are as follows
- Call the Submit method to pass in the runnable or callable object
- Determines whether the passed-in object is null, throws an exception, and does not continue the process as null
- Converts an incoming object to a Runnablefuture object
- Executes the Execute method, passing in the Runnablefuture object
- Returns the Runnablefuture object
The flowchart is as follows
Execute
public void execute(Runnable command) { //传进来的线程为null,则抛出空指针异常 if (command == null) throw new NullPointerException(); //获取当前线程池的状态+线程个数变量 int c = ctl.get(); /** * 3个步骤 */ //1.判断当前线程池线程个数是否小于corePoolSize,小于则调用addWorker方法创建新线程运行,且传进来的Runnable当做第一个任务执行。 //如果调用addWorker方法返回false,则直接返回 if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } //2.如果线程池处于RUNNING状态,则添加任务到阻塞队列 if (isRunning(c) && workQueue.offer(command)) { //二次检查 int recheck = ctl.get(); //如果当前线程池状态不是RUNNING则从队列删除任务,并执行拒绝策略 if (! isRunning(recheck) && remove(command)) reject(command); //否者如果当前线程池线程空,则添加一个线程 else if (workerCountOf(recheck) == 0) addWorker(null, false); } //3.新增线程,新增失败则执行拒绝策略 else if (!addWorker(command, false)) reject(command);}
In fact, from the above code comments can be seen in the three judgments,
- Whether the number of core threads is full
- Whether the queue is full
- Whether the thread pool is full
Then, depending on the three conditions, the main processing flow of the thread pool in the art Book of Java concurrent programming may be easier to understand
Here are the detailed steps for the entire process
- Call the Execute method, passing in the Runable object
- Determines whether the passed-in object is null, throws an exception, and does not continue the process as null
- Gets the status of the current thread pool and the number of threads variable
- Determine whether the current number of threads is less than the number of core threads, is to go through process 5, otherwise walk process 6
- Add the number of threads, add success ends, and fail to retrieve the current thread pool state and thread count variables.
- Determine if the thread pool is in the running state, add the task to the blocking queue, otherwise go to process 10, add the task success continues the process 7
- Regain status and thread count variables for the current thread pool
- Recheck the thread pool state, not the running state remove the previously added task, there is a false walk process 9, all true then go to process 11
- Check if thread pool threads are 0, otherwise end the process, call Addworker (null, FALSE), and then end
- Call!addworker (command, False), true to go process 11,false then end
- Call Deny policy reject (command), end
May see the above will be a bit around, not clear can see the following flowchart
Addworker
Private Boolean Addworker (Runnable Firsttask, Boolean core) {retry:for (;;) {int c = ctl.get (); int rs = runstateof (c); Check that the current thread pool state is shutdown, STOP, tidying, or terminated//and! (The current state is SHUTDOWN, and the incoming task is null, and the queue is not NULL)//The condition is set to return False if (Rs >= SHUTDOWN &&!) (rs = = SHUTDOWN && Firsttask = = null &&! workqueue.isempty ())) return false; loop for (;;) {int WC = Workercountof (c); Number of core threads if the current number of threads exceeds the maximum capacity or is greater (depending on whether the incoming core is the core thread count or the maximum number of threads) | | The maximum number of threads, which returns False if (WC >= Capacity | | WC >= (Core corepoolsize:maximumpoolsize)) return false; CAS increases C, and success jumps out of retry if (Compareandincrementworkercount (c)) break retry; CAS failed to perform the following method to see if the current number of threads has changed, and the change continues to retry the loop, without changing the internal loop C = Ctl.get (); Re-read CTL if (runstateof (c)! = RS) Continue retry; }}//cas Successful Boolean workerstarted = false; Boolean workeradded = false; Worker w = null; try {//Create a new thread w = new Worker (Firsttask); Final Thread t = w.thread; if (t! = null) {//locking final Reentrantlock mainlock = This.mainlock; Mainlock.lock (); try {//re-check thread pool status//Avoid threadfactory exit failure or lock get front pool closed int rs = Runstateof (Ctl.get ()); if (Rs < SHUTDOWN | | (rs = = SHUTDOWN && Firsttask = = null)) {if (t.isalive ())//check if the thread is a bootable throw new illegalthreadstateexception () first; Workers.add (w); int s = workers.size (); if (S > largestpoolsize) largestpoolsize = s; Workeradded = true; }} finally {mainlock.uNlock (); }//To determine if the worker was added successfully, start the thread successfully, and then set workerstarted to True if (workeradded) {T.start (); Workerstarted = true; }}} finally {//To determine if the thread has successfully started or not, then call the Addworkerfailed method if (! workerstarted) Addworkerfail Ed (w); } return workerstarted;}
Here you can divide the addworker into two parts, the first part increases the number of thread pools, and the second part adds the task to Workder and executes.
The first part is mainly two cycles, the outer loop is to judge the thread pool state, and the following describes the principle of threadpoolexecutor from Java thread pool
rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())
Expand After the operation is equivalent to
s >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
In other words, false is returned in the following cases:
- Current thread pool status is stop,tidying,terminated
- The current thread pool state is shutdown and already has the first task
- The current thread pool status is shutdown and the task queue is empty
The inner loop function is to use CAs to increase the number of threads, if the number of threads overrun returns false, whether the Cas,cas successful exit double loop, whether the CAS failed, to see when the state of the front pool is changed, if changed, then re-enter the outer loop to regain the thread pool state, No person enters the inner loop to continue with the CAS attempt.
To the second part of the success of the CAs, that is, the number of threads added one, but now the task has not begun to execute, where the use of global exclusive lock to control workers inside the add task, in fact, can also use the concurrency of security set, but performance is not exclusive lock (this is known from the note). It is important to note that the status of the thread pool is re-examined after acquiring the lock, because other threads may change the state of the thread pool before this method acquires the lock, such as calling the Shutdown method. Adding success initiates the task execution.
So there are two parts to describe the flowchart.
First part flowchart
Part Two flowchart
Worker Object
The worker is the Finnal class defined in Threadpoolexecutor, which inherits the Abstractqueuedsynchronizer class and implements the Runnable interface, where the Run method is as follows
public void run() { runWorker(this);}
The Runworker method is called when the thread starts, and other aspects of the class are not described here.
Runworker
final void Runworker (Worker w) {Thread wt = Thread.CurrentThread (); Runnable task = W.firsttask; W.firsttask = null; W.unlock (); Boolean completedabruptly = true; try {//Loop get task while (task! = NULL | | (Task = Gettask ()) = null) {W.lock (); When the thread pool is in the stop state or tidying, terminated state, set the current thread to be in a break state//If not, the current thread is in the running or shutdown state, ensuring that the current thread is not in a broken state Recheck whether the state of the current thread pool is greater than or equal to the STOP state if (Runstateatleast (Ctl.get (), stop) | | (thread.interrupted () && runstateatleast (Ctl.get (), STOP))) &&!wt.isinterrupted ()) wt.interrupt (); try {//provided to the inheriting class to do some statistics and the like, call BeforeExecute (WT, Task) before the thread runs; 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 {//provided to the inheriting class to use to do some sort of thing, call AfterExecute (task, thrown) after the thread runs; }} finally {task = null; Statistics the current number of worker completed the task w.completedtasks++; W.unlock (); }} completedabruptly = false; } finally {//the entire thread ends when called and the thread exits the operation. Work Processworkerexit (W, completedabruptly) to count the number of tasks completed by the entire thread pool; }}
Gettask
The main function of the Gettask method is actually from the method name can be seen, is to get the task
Private Runnable Gettask () {Boolean timedout = false;//Did the last poll () Time out? loop for (;;) {int c = ctl.get (); int rs = runstateof (c); Thread pool status and whether the queue is empty if (Rs >= SHUTDOWN && (rs >= STOP | | workqueue.isempty ())) {Decrementwor Kercount (); return null; }//number of threads int WC = Workercountof (c); Boolean timed = Allowcorethreadtimeout | | WC > corepoolsize; (whether the current number of threads is greater than the maximum number of threads, or)//and (the number of threads is greater than 1 or the task queue is empty)//There is a problem (timed && timedout) TimedOut = False, as if (timed && Amp timedout) It's always been false. if (WC > Maximumpoolsize | | (timed && timedout)) && (WC > 1 | | workqueue.isempty ())) {if (Compareanddecrementworkercount (c)) return n Ull Continue try {//Get task Runnable r = timed? Workqueue.poll (KeepAliveTime, timeunit.nanoseconds): Workqueue.take (); if (r! = null) return r; TimedOut = true; } catch (Interruptedexception retry) {timedout = false; } }}
Close the thread pool shutdown
When the shutdown method is called, the thread pool will no longer receive new tasks and then complete the task that was previously placed in the queue.
Here is the source code for the Shutdown method
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate();}
Shutdownnow
Immediately stops all execution tasks and returns the tasks in the queue
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;}
Shutdown and Shutdownnow differences
The two methods of shutdown and Shutdownnow are to close the thread pool, the process is roughly the same, only a few steps are different, as follows
- Locking
- Check shutdown permissions
- CAS changing thread pool state
- Set the interrupt flag (the thread pool is not receiving a task, the queue task is completed)/interrupts the currently executing threads
- Call the OnShutdown method (the method provided to the subclass)/Get the task in the queue
- Unlock
- Attempt to change the thread pool state to a terminating state terminated
- End/return tasks in a queue
Summarize
The thread pool can provide great convenience to our multithreaded coding, as is the case with database connection pooling, which reduces the overhead of threads and provides the reuse of threads. And Threadpoolexecutor also provides some of the methods that we can use, such as BeforeExecute, AfterExecute, and so on, to further manage and count threads through these methods.
When using the thread pool, it is important to note that the submitted thread tasks can be divided into CPU 密集型任务
and IO 密集型任务
assigned different threads depending on the task.
- CPU-intensive tasks:
- Fewer threads should be allocated, such as
CPU
the size of the number equivalent
- IO-intensive tasks:
- Because threads are not always running, you can configure as many threads as possible, such as the number of CPUs * 2
- Mixed-type tasks:
- It can be split into
CPU
intensive tasks and IO
intensive tasks, which are configured separately.
Well, this blog post here is over, there may be some flaws in the text, welcome to leave a message.
If this article is helpful to you, give a star a chant, thank you. This article GitHub address: point here dot here
Resources
- Research on threadpoolexecutor principle of-java thread pool in concurrent programming network
- The art of Java concurrent programming
Java Multithreading series: Threadpoolexecutor Source Analysis