First, preface
The greatest advantage of the executor framework introduced after 1.5 is the decoupling of task submission and execution. The person who wants to perform the task simply describes the task clearly and submits it. How the task is executed, who executes it, and when it is executed, the person who commits it does not care. Specifically, submitting a callable object to Executorservice (such as the most commonly used thread pool threadpoolexecutor) will result in a future object that calls the Get method of the future object to wait for the results to be executed.
With this encapsulation, the process of submitting a task to obtain results is greatly simplified for the user, and the caller can wait for the execution result directly from where it was submitted. The biggest effect of encapsulation is to make the threads that actually perform the task become unknown. Have you ever felt this scene déjà vu? We work as the eldest brother (and called ld^2) to give a task to our boss (LD), whether it is LD to do their own work, or turn around to pull a bunch of bitter brothers working overtime, that ld^2 is no matter. Ld^2 only used to describe the person clearly to LD, and then drank coffee waiting to receive LD's report. such as LD an e-mail very elegant report ld^2report results, the actual operation is yards nong A and yard B dry for one months, or yards Nong Abcde overtime work for a week, mostly do not reflect. The advantage of this mechanism is that ld^2 find a suitable LD to submit the task, the interface is friendly and effective, not for the specific how to do the trouble.
The simplest example of second
It looks like this is the process of execution. Call this code is the boss of the eldest brother, all he needs to do is to find a suitable boss (as the following example LAODAA is honored to be selected), submit the task is good.
A thread pool with 7 job threads, boss's eldest brother found a small team of 7 men Executorservice Laodaa = Executors.newfixedthreadpool (7);//Submit homework to Boss, The job content is encapsulated in callable, and the type of output is a string. String outputs = Laoda.submit ( new callable<string> () {public String call () throws Exception { Return "I am a task, which submited by the so-called Laoda, and run by those anonymous workers" ; After the submission is waiting for the results, in the end is 7 of the workers who led the task, the boss is not concerned. }). Get (); SYSTEM.OUT.PRINTLN (outputs);
It is very simple to use, in fact, there are only two lines of statements to complete all the functions: Create a thread pool, commit the task, and wait for the execution result.
In the example, the generated thread pool employs a static method of tool class executors. In addition to Newfixedthreadpool can generate a fixed-size thread pool, Newcachedthreadpool can generate an unbounded, automatically recycled thread pool. Newsinglethreadscheduledexecutor can generate a thread pool for a single thread. Newscheduledthreadpool can also generate thread pools that support periodic tasks. In general user scenarios, the various setup requirements of the thread pool can be generated in such a way that you do not have to use your new thread pools.
Three, Code analysis
This set of mechanism how to use, the above two sentences to do, very convenient and friendly. But how did the submit task be executed? Who executed it? How to do this when the call is only waiting for execution to finish before get to the result. These are 1.5 after the executor interface of the thread pool, the future interface to achieve the results of the task, with the Aqs and the original runnable to do. In the following sections we try to understand the task commit, task execution, and get the task execution results by dissecting each part of the code to see several major steps. In order to control the space, highlight the main logic, the code snippet referenced in the article removed the exception capture, non-main condition judgment, non-primary operation. In this paper, just the most commonly used threadpoolexecutor thread pool example, in fact, the Executorservice interface defines a lot of features rich other types, have their own characteristics, but similar style. The focus of this article is to introduce the task submission process, the process involved in the Executorservice, Threadpoolexecutor, AQS, future, Futuretask, etc. will only describe the process used in the content, will not be detailed for each class to expand.
1. Task Submission
As you can see from the class diagram, the interface Executorservice inherits from executor. Unlike executor, which only defines a method to perform a task, in Executorservice, as its name implies, defines a service that defines the behavior of the full thread pool, accepting the task of committing, executing, and shutting down the service. The abstract class Abstractexecutorservice class implements the Executorservice interface and implements the default behavior of the interface definition.
(Click to enlarge image)
The Submit method submitted by the Abstractexecutorservice task has three implementations. The first receives a runnable task without executing the result; the second one is two parameters: a task, a result of execution, and a third callable, which itself contains the contents of the task and the result of execution. The return result of the Submit method is the future type, which is called by the Get method defined by the interface to obtain the execution result. The return value type V of the V Get () method is agreed upon when the task is submitted.
In addition to the method of the submit task, as a service management, the Executorservice interface also defines the service shutdown method shutdown and the Shutdownnow method, which can gently or immediately shut down the execution service. Subclasses that implement the method support the definition according to its own characteristics. In Threadpoolexecutor, we maintain the running, SHUTDOWN, STOP, terminated four states to achieve the management of the thread pool. The complete operation mechanism of thread pool is not the focus of this article, but also focuses on the logic in the submit process.
1) Look at the Code submission section in Abstractexecutorservice, and after constructing the good one Futuretask object, call the Execute () method to perform the task. We know that this method is the most important method defined in the top-level interface executor: The Futuretask type implements the Runnable interface and therefore satisfies the conventions of the Execute () method in executor. It is also interesting that the object is returned as the return value of the Submit method after execute execution, because Futuretask also implements the future interface, which satisfies the contract of the future interface.
Public <T> future<t> Submit (callable<t> Task) { if (task = = null) throw new NullPointerException (); runnablefuture<t> ftask = newtaskfor (Task); Execute (ftask); return ftask; }
2) Submit parameters are encapsulated into the Futuretask type to execute, corresponding to the previous three different parameter types will be encapsulated into futuretask.
Protected <T> runnablefuture<t> newtaskfor (callable<t> callable) { return new futuretask<t > (callable); }
3) The function of the Execute method defined in the executor interface is to perform the submitted task, which is not implemented in the abstract class Abstractexecutorservice and is persisted to the subclass. We look at the Threadpoolexecutor, how the most extensive thread pool is used to execute those tasks that are submit. This method looks relatively simple, but when the thread pool creates a new job thread to handle the task, when only the receiving task does not create the job thread, and when the task is rejected. The thread pool's receive tasks, and the policies that maintain worker threads, are reflected in them.
As a prerequisite, add the following threadpoolexecutor with the two most important collection properties, which are the task queue that stores the receiving task and the set of jobs used to work.
Task Queue Private final blockingqueue<runnable> workqueue;//job thread collection private final hashset<worker> workers = new Hashset<worker> ();
Where the blocking queue Workqueue is to store the task to be executed, the thread pool can be constructed with the option to satisfy the synchronousqueue of the Blockingqueue interface definition, Linkedblockingqueue or delayedworkqueue different blocking queues to implement different characteristics of the thread pool.
Focus on the Addifundercorepoolsize,workqueue.offer (command) that is called in the execute (Runnable command) method, ensurequeuedtaskhandled (command), addifundermaximumpoolsize (command) these operations. In particular, several long-name private methods, the method name of the hump-type words, coupled with the other side of the law in the context of understanding can understand its function.
Because a few of the previous methods in the inside that is the operation, and return a Boolean value, affecting the subsequent logic, it is not convenient in the method body for each statement annotated to illustrate, need to be roughly related to see. So first of all, the main logic of the Execute method is described, and then the function of the respective methods is seen.
- If the thread pool's state is running, the size of the thread pool is less than the number of core threads configured, and the description can also create a new thread, start a new thread to perform this task.
- If the thread pool's state is running, the size of the thread pool is less than the maximum number of threads configured, and the task queue is full, indicating that the existing thread cannot support the current task, and that the thread pool has space to continue to expand, you can create a new thread to handle the submitted task.
- If the status of the thread pool is running, the current thread pool size is greater than or equal to the configured number of core threads, stating that the current number of threads configured is sufficient, instead of creating a new thread, simply add the task to the task queue. If the task queue is dissatisfied, the submitted task waits for processing in the task queue, and if the task queue is full, you need to consider whether you want to expand the capacity of the thread pool.
- When the thread pool is closed or the above conditions are not met, a deny policy is made, and the deny policy is defined in the Rejectedexecutionhandler interface and can have many different implementations.
The above is actually the main idea of the analysis, detailed deployment may be more complex. Simple combing the idea: when the thread pool is built, a nominal size is defined, when the number of thread Cheng work threads is less than the rated size, a new task comes in to create a new worker thread, if the threshold is exceeded, it is generally not created, but the receiving task is added to the task queue. But if there are too many tasks in the task queue, ask for additional worker threads to help. Denial of service if it is still insufficient. This scene is actually a scene that we will encounter in our work every day. We control the boss, the hand has a certain HC (Head Count), when the top boss has Woufei down, the hands of people is not enough, but not more than HC, we recruit ourselves; if more than or busy, then to the door to the boss to apply for seconded staff to help; If you can not finish, then there is no way, We will not answer the new task.
public void Execute (Runnable command) { if (command = = null) throw new NullPointerException (); if (poolsize >= corepoolsize | |!addifundercorepoolsize (command)) { if (runstate = = RUNNING && workqueue.o Ffer (command)) { if (runstate! = RUNNING | | poolsize = = 0) ensurequeuedtaskhandled (command); } else if (!addifundermaximumpoolsize command) reject (command);//is shutdown or saturated }}
4) Addifundercorepoolsize Method Check if the size of the current thread pool is less than the number of core threads configured, the description can also create a new thread, then start a new thread to perform this task.
Private Boolean addifundercorepoolsize (Runnable firsttask) { Thread t = null; If the size of the current thread pool is less than the number of core threads configured, the description can also create a new thread if (Poolsize < corepoolsize && Runstate = = RUNNING) // Then start a new thread to perform this task t = addthread (firsttask); return T! = null; }
5) Similar to the previous method, Addifundermaximumpoolsize checks if the size of the thread pool is less than the maximum number of threads configured and the task queue is full (the Execute method attempts to join the current thread to the task queue is unsuccessful). Indicates that an existing thread cannot support the current task, but the thread pool has space to continue expanding, and a new thread can be created to handle the submitted task.
Private Boolean addifundermaximumpoolsize (Runnable firsttask) { //If the size of the thread pool is less than the maximum number of threads configured, and the task queue is full (that is, the Execute method attempts to join the current thread to the task queue Workqueue.offer), indicating that the existing thread has not been able to support the current task, but that the thread pool has the space to continue expanding if ( Poolsize < maximumpoolsize && Runstate = = RUNNING) //You can create a new thread to handle the committed task t = addthread (Firsttask) ; return T! = null; }
6) in the Ensurequeuedtaskhandled method, judging if the current state is not runing, the current task is not added to the task queue, judging if the state is stopped, the number of threads is less than the maximum allowed, and the task queue is not empty, A new worker thread is added to the thread pool to help deal with tasks that have not yet been completed.
private void ensurequeuedtaskhandled (Runnable command) { // If the current state is not runing, the current task is not added to the task queue, and if the state is stopped, The number of threads is less than the maximum number allowed, and the task queue is not empty if (state < STOP && poolsize < Math.max (corepoolsize, 1) && ! Workqueue.isempty ()) //Add a new worker thread to the thread pool to help deal with tasks that have not yet been processed t = addthread (null); if (reject) reject (command); }
7) In the previous method, the Adthread method is called to create a worker thread, and the difference is that some of the worker threads created are associated with the received task Firsttask, and some do not. This method creates a worker for the currently received task Firsttask, adds the worker to the job collection hashset<worker> workers, and starts the job.
Private Thread Addthread (Runnable firsttask) { //For the currently received task Firsttask create worker worker W = new Worker (Firsttask); C2/>thread t = threadfactory.newthread (w); W.thread = t; Add the Worker to the job collection hashset<worker> workers, and start the job Workers.add (w); T.start (); return t; }
At this point, the task submission process is simply described, and the main response logic of the Executorservice framework thread pool after the task is submitted is to receive the task, create or maintain the management thread as needed.
What does it take to maintain these worker threads? First do not look at the code behind, think of our boss every month hard to hand over the boss's generous salary to our hands, and regularly take everyone out of the happy, but also the regular care of personal life, all do these are for what? The hard-working code worker does not move to this side of the brain, but guess still can guess, let work Bai. This article wants to focus on the details, such as how the worker of the line constructor works, whether the task is executed in these worker threads, and how to ensure that after the execution is done, the boss outside waiting for the task gets the desired result, which we'll cover in detail in the next article.
Article Source: http://www.infoq.com/cn/articles/executor-framework-thread-pool-task-execution-part-01
Play (fine) say Executor framework thread pool task execution whole process (top)