Java Thread Pooling Implementation principle

Source: Internet
Author: User

(1): What states are there in the thread pool and how are these states switched between them?

(2): What are the types of thread pools?

(3): What parameters are required to create a thread pool, and what are the specific meanings of these parameters?

(4): Run the process after adding a task to the thread pool?

(5): How does the thread pool reuse threads?

(6): Shutdown of thread pool

First answer the first question: what is the status of the thread pool;

View Threadpoolexecutor source code will know:

//runstate is stored in the High-order bits   Private Static Final intRUNNING =-1 <<count_bits; Private Static Final intSHUTDOWN = 0 <<count_bits; Private Static Final intSTOP = 1 <<count_bits; Private Static Final inttidying = 2 <<count_bits; Private Static Final intTERMINATED = 3 << count_bits;

There are 5 states:

<1>running: Can accept new tasks, but also can handle the tasks inside the blocking queue;

<2>shutdown: Can not accept new tasks, but can handle the tasks in the blocking queue;

<3>stop: No new tasks can be accepted, and the tasks in the blocking queue are not handled, while the tasks being processed are interrupted;

<4>tidying: belongs to the transition phase, at which point all tasks have been executed, there are no valid threads in the current thread pool, and the terminated method will be called;

<5>terminated: Terminating state, which is the state after the call to the Terminated method;

So how do you convert between these 5 states? See the Threadpoolexecutor source code inside the comments can be known:

* RUNNING- SHUTDOWN       * on    invocation of SHUTDOWN (), perhaps implicitly in Finalize ()       * (RUNNING or SHUTDOWN), STOP       * on    invocation of Shutdownnow ()       * SHUTDOWN  Tidying       * When    both queue and pool is empty       * STOP-- tidying       * When     pool is empty       * Tidying- TERMINATED       * When the    TERMINATED () hook Method has completed  

As can be seen from the above, the thread pool state is converted from running to shutdown when calling the shutdown method, and when the Shutdownnow method is called, the thread pool state is converted from Running/shutdown to stop In the case where the blocking queue is empty while the thread pool is empty, the thread pool state is converted from shutdown to tidying; the thread pool state is converted from stop to tidying when the online pool is empty, and when the terminated method is called, The thread pool state is converted from tidying to terminate;

After we understand how the thread pool is switching between state and state, let's take a look at the second question, the type of thread pool:

(1): Cachedthreadpool: Cache thread pool, the number of threads in such thread pools is indeterminate, theoretically can reach integer.max_value, this thread pool thread is non-core thread, since it is non-core thread, there is a time-out elimination mechanism, When a thread inside is idle for longer than the set timeout, the thread is recycled;

Allowcorethreadtimeout

(3): Scheduledthreadpool: The task thread pool, the number of core threads in the thread pools is fixed, and the number of non-core threads is unlimited, and for non-core threads there is a time-out elimination mechanism, mainly for the execution of timed tasks or periodic tasks of the scene;

(4): Singlethreadpool: Single thread pool, the thread pool, there is only one threads, and there is no non-core thread, it feels like a special version of Fixedthreadpool, he is mainly used to ensure that the task in the same thread in the order of execution, a bit similar to synchronize it;

Next we look at the third question: What parameters are required to create a thread pool?

Also view the Threadpoolexecutor source code to see the constructor that creates the thread pool:

 Public Threadpoolexecutor (int  corepoolsize,                                int  maximumpoolsize,                                Long  KeepAliveTime,                                timeunit unit,                                blockingqueue<Runnable> workQueue,                                Threadfactory threadfactory,                                rejectedexecutionhandler handler)  

Regardless of which constructor you call Threadpoolexecutor, it will eventually be executed to this constructor, which has 7 parameters, it is precisely because of the assignment of these 7 parameter values, resulting in a different type of thread pool, Like our common cachedthreadpoolexecutor, fixedthreadpoolexecutor.

Singlethreadpoolexecutor, Scheduledthreadpoolexecutor, we always look at the specific meaning of these parameters:

<1>corepoolsize: The number of core threads in the thread pool; When a task is submitted to the pool, the line pool creates a thread to perform the task, even if there are other idle threads that are not created until the number of threads reaches Corepoolsize. This time the new task is put into the blocking queue, and if the thread pool's Prestartallcorethreads method is called, the core thread will be initialized when the thread pool is created;

<2>maximumpoolsize: The maximum number of threads allowed to be created by the thread pool, if the blocking queue is full and the number of threads that have been created is less than the maximum number of threads, a new thread is created to handle the tasks in the blocking queue;

<3>keepalivetime: Thread activity hold time refers to the time that the worker thread is idle to survive, and by default this parameter only works if the number of threads is greater than corepoolsize. That is, when the number of threads in a thread pool is greater than corepoolsize, if a thread's idle time reaches KeepAliveTime, the thread is terminated until the number of threads in the thread pool is not greater than corepoolsize If you call Allowcorethreadtimeout, the number of threads in the thread pool is not greater than corepoolsize, the KeepAliveTime parameter can also work, knowing that the number is 0;

<4>unit: The time unit of the parameter KeepAliveTime;

<5>workqueue: Blocking queue; for storing pending tasks, there are four types of blocking queues, Arrayblockingqueue (array-based bounded blocking queue), Linkedblockingqueue (blocking queue based on linked list structure) , Synchronousqueue (blocking queue that does not store elements), Priorityblockingqueue (a blocking queue with priority);

<6>threadfactory: The thread factory used to create threads;

<7>handler: When the blocking queue is full and there are no idle threads, that is, the number of threads in the thread pool has reached the maximum number of threads and is saturated, a strategy must be taken to handle the newly committed task, and we can define our own processing policy. You can also use the strategy that the system has provided to us, first look at the 4 strategies that the system provides for us, AbortPolicy (throwing exceptions directly), Callerrunspolicy (only the thread where the caller is running), Discardoldestpolicy ( Discard the most recent task in the blocking queue and perform the current task), Discard (direct discard);

The next step is to add the task to the thread pool after the running process;

We can call the Submit or Execute method, the biggest difference is that we can call the Submit method, we could pass in an object that implements the callable interface, and then we can get the return value of the task through the future object after the completion of the current task execution. The Execute method is actually executed inside the submit, but it is not possible to get the return value after the execution of the task is called, and if the Submit method is called, it can throw an exception, but the Execute method is called. The exception is digested in its interior, that is, the exception is processed within it and is not transmitted outward;

Because the Submit method will eventually execute the Execute method, we just need to understand the Execute method:

There are three situations within the Execute method to handle:

<1>: First, determine whether the number of threads in the current thread pool is less than corepoolsize, and if less, create a new worker object directly from the Addworker method to perform our current task;

<2>: If the number of threads in the current thread pool is greater than corepoolsize, then an attempt is being taken to add the current task to the blocking queue and then check the status of the thread pool for the second time, and if the thread pool is not in running state, the task just added to the blocking queue will be moved out , while rejecting the current task request, if the second check finds that the current thread pool is in the running state, the number of worker threads in the current thread pool is checked for 0, and if 0, a worker object is created by the Addworker method to handle the task in the blocking queue;

<3>: If the original thread pool is not in the running state or if we just have an error adding the current task to the blocking queue, then we will try to create a new worker through Addworker to handle the current task, if the addition fails, The current task request is rejected;

As you can see in the Execute method above, we just check that the number of threads in the current thread pool is more than corepoolsize, so when the number of threads in the thread pool is more than maximumpoolsize, where is it detected? Actually in the Addworker method, we can look at the code inside the Addworker:

if (WC >= Capacity | | >= (Core corepoolsize:maximumpoolsize))                                              return false;  

If the current number of threads exceeds Maximumpoolsize, the return method is called directly, returning false;

In fact, it is clear to us that the number of threads in a thread pool is actually the number of workers in this thread pool, and if the worker is larger than corepoolsize, then the task is in the blocking queue, and the worker is a wrapper class for our task in Java , his statement was Jiangzi:

Private Final class Worker          extends Abstractqueuedsynchronizer          Implements Runnable

You can see that he implements the Runnable interface, which was created in the Addworker method through the new Worker (Firsttask), and we'll see what his constructor looks like:

Worker (Runnable firsttask) {              setState (//  inhibit interrupts until Runworker  this            . firsttask = firsttask;               this. Thread = Getthreadfactory (). Newthread (this);          

The firsttask here is actually the argument that we passed in when we called execute or submit, in general these parameters are implemented callable or runnable interface;

After the worker object is created through the Addworker method, the method finally executes the start method of the thread property of the worker, and the thread property is actually the thread that encapsulates the worker, Executing his start method actually executes the worker's Run method, because the worker implements the Runnable interface and executes the Runworker method inside the Run method. The Runworker method will first determine whether the task we are passing in is empty or empty, and will execute the task we submitted via execute or the Submit method. Note that although we will commit to implement the callable interface object through the Submit method, but when the Submit method is called, the callable object is actually encapsulated to implement the Runnable interface object, Don't believe us see how the Submit method source code is implemented:

 public  <T> future<t> Submit (Callable<t> Task) { if  (Task = = null) throw  new        NullPointerException ();      Runnablefuture  <T> ftask = newtaskfor (Task);      Execute (ftask);   return   Ftask; }  

See, in fact, when you pass the implementation of the callable interface object, in the Submit method will be encapsulated as Runnablefuture object, and Runnablefuture interface is inherited runnable interface Then, plainly speaking, it is the run method that directly executes our task, and if it is empty, a task is taken out of the blocking queue through the Gettask method, and the task continues to be taken from the blocking queue after the execution of the task is completed. The Runworker internal loop is exited until the return value of Gettask is empty, so what happens when Gettask returns empty? Check the source code comment for the Gettask method to know: Gettask will return empty if the worker has to quit, and in what case will the worker quit? (1): When the number of workers exceeds Maximumpoolsize, (2): When the thread pool status is stop, (3): When the thread pool status is shutdown and the blocking queue is empty; (4): Use the Wait timeout time to take data from the blocking queue. But the data is still not received after the timeout;

If the Runworker method exits the loop inside it, it means that there is no task to execute in the current blocking queue, and you can see that the Processworkerexit method is executed in the finally statement block inside the Runworker method. Used to recycle a worker object, this method passes in a parameter that represents the worker object that needs to be deleted, and invokes the Tryterminate method to try to close the thread pool when the worker is recycled. In the Tryterminate method will check whether a worker is working, check the status of the thread pool, no problem will be the current thread pool state transition to tidying, and then call terminated method, the thread pool state to update to terminated;

From the above analysis, we can see the 4 phases of the thread pool running:

(1): Poolsize < Corepoolsize, a new thread (core thread) is created directly to perform the currently committed task;

(2): Poolsize = Corepoolsize, and the blocking queue is not full at this time, the current task will be added to the blocking queue, if there is a worker thread (non-core thread), then the worker thread will handle the task in the blocking queue, if the number of worker threads at this time is 0 , then a worker thread (non-core thread) is created;

(3): Poolsize = Corepoolsize, and when the blocking queue is full, new worker threads (non-core threads) are created directly to handle the tasks in the blocking queue;

(4): Poolsize = Maximumpoolsize, and when the blocking queue is full, then the rejection mechanism is triggered, The specific rejection strategy is to see what we have to create threadpoolexecutor when the Rejectexecutionhandler parameters passed;

The next step is how the thread pool is reusing threads.

Personally think that the thread pool inside the reuse of the work is implemented in the gettask inside, in the gettask there are two for the dead loop nesting, he will continue to remove from the blocking column inside the task to be performed, returned to our Runworker method inside, In the Runworker method, as long as the Gettask returned task is not empty will execute the task of the Run method to handle it, so continue to execute until Gettask return empty, the situation is that the blocking queue there is no task, When a thread finishes a task and then processes another task in the blocking queue, the threads in the thread pool can concurrently handle the tasks in the blocking queue, and at the end of the day, when there are no tasks inside the blocking queue, it is time to determine if the worker object needs to be recycled. In fact, the number of worker objects is the number of threads in the thread pool, as to what needs to be recycled, above already said, is four kinds of situation;

And finally, how did the thread pool get shut down?

When it comes to the closure of the thread pool, it takes two methods, shutdown and Shutdownnow, all in Threadpoolexecutor, and for shutdown, he switches the thread pool state to shutdown, At this point, the tasks in the blocking queue are not affected, but the newly added tasks are rejected, while the idle worker is reclaimed, while the Shutdownnow method switches the thread pool state to stop, and the tasks in the blocking queue are not processed, and the newly added tasks are not processed. , and all workers are recycled;

Java Thread Pooling Implementation principle

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.