Java Multithreading series: Threadpoolexecutor Source Analysis

Source: Internet
Author: User
Tags cas connection pooling throwable

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

    1. Call the Submit method to pass in the runnable or callable object
    2. Determines whether the passed-in object is null, throws an exception, and does not continue the process as null
    3. Converts an incoming object to a Runnablefuture object
    4. Executes the Execute method, passing in the Runnablefuture object
    5. 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,

    1. Whether the number of core threads is full
    2. Whether the queue is full
    3. 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

    1. Call the Execute method, passing in the Runable object
    2. Determines whether the passed-in object is null, throws an exception, and does not continue the process as null
    3. Gets the status of the current thread pool and the number of threads variable
    4. 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
    5. Add the number of threads, add success ends, and fail to retrieve the current thread pool state and thread count variables.
    6. 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
    7. Regain status and thread count variables for the current thread pool
    8. 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
    9. Check if thread pool threads are 0, otherwise end the process, call Addworker (null, FALSE), and then end
    10. Call!addworker (command, False), true to go process 11,false then end
    11. 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

    1. Locking
    2. Check shutdown permissions
    3. CAS changing thread pool state
    4. Set the interrupt flag (the thread pool is not receiving a task, the queue task is completed)/interrupts the currently executing threads
    5. Call the OnShutdown method (the method provided to the subclass)/Get the task in the queue
    6. Unlock
    7. Attempt to change the thread pool state to a terminating state terminated
    8. 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
    1. Research on threadpoolexecutor principle of-java thread pool in concurrent programming network
    2. The art of Java concurrent programming

Java Multithreading series: Threadpoolexecutor Source Analysis

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.