Ali's interviewer asked a question, if corepollsize=10,maxpollsize=20, if there were 25 threads to do,
Answer: Of course the first place in the blocking queue (if the number is 0, has been waiting, Linkedblockingdeque is a chain list structure of the two-way blocking queue, both sides can enter and leave, the kind,
Reference: Chat concurrency (vii)--java in the blocking queue) inside, Blockingqueue, the interviewer wants to know the specific process flow, I mastered the not deep, so determined to look carefully:
Especially the example of the worker in the workshop, take a good look and understand that threading is useful:
In the previous chapter we outlined the thread pool, which we'll look at to create the source code for the Newfixedthreadpool. The example is also the one we wrote in the previous chapter.
Ways to create Newfixedthreadpool:
Public Static Executorservice newfixedthreadpool (int nthreads) { returnnew Threadpoolexecutor (Nthreads, nthreads, 0L, timeunit.milliseconds, new Linkedblockingqueue<runnable>()); }
Public Static Executorservice newfixedthreadpool (int nthreads, threadfactory threadfactory) { return New threadpoolexecutor (nthreads, nthreads, 0L, timeunit.milliseconds, New Linkedblockingqueue<runnable>(), threadfactory); }
The above two methods are two ways of creating a fixed number of thread pools, the difference being that the second method of creating a thread factory is more than one. Let's continue to look at the constructors in this class Threadpoolexecutor:
Threadpoolexecutor constructor:
Public Threadpoolexecutor (int corepoolsize, int maximumpoolsize, Long KeepAliveTime, timeunit unit, blockingqueue<Runnable> workQueue) { This (Corepoolsize, maximumpoolsize, KeepAliveTime, Unit, WorkQueue, executors.defaultthreadfactory (), DefaultHandler); }
PublicThreadpoolexecutor (intCorepoolsize,intMaximumpoolsize,LongKeepAliveTime, timeunit unit, Blockingqueue<Runnable>WorkQueue, Threadfactory threadfactory, Rejectedexecutionhandle R handler) {if(Corepoolsize < 0 | |maximumpoolsize<= 0 | |maximumpoolsize< Corepoolsize | |KeepAliveTime< 0) Throw Newillegalargumentexception (); if(WorkQueue = =NULL|| Threadfactory = =NULL|| Handler = =NULL) Throw NewNullPointerException (); This. corepoolsize =corepoolsize; This. maximumpoolsize =maximumpoolsize; This. WorkQueue =WorkQueue; This. KeepAliveTime =Unit.tonanos (KeepAliveTime); This. Threadfactory =threadfactory; This. Handler =handler; }
All of the constructors in Threadpollexecutor will eventually call the above constructor, and then we'll look at what these parameters mean:
Corepoolsize: The minimum number of threads that are persisted in the pool after the thread pool is started. It is necessary to note that the number of threads is progressively reaching the corepoolsize value. For example, if Corepoolsize is set to 10 and the number of tasks is only 5, the thread pool will start with a maximum of 5 threads instead of starting 10 threads at once. Maxinumpoolsize:
The maximum number of threads that can be accommodated in a thread pool, and if exceeded, use Rejectedexecutionhandler to reject policy processing.
KeepAliveTime: The maximum life cycle of a thread. The life cycle here has two constraints: one: This parameter is for threads that exceed the number of corepoolsize, and two: threads that are in a non-running state. For example, if corepoolsize (minimum number of threads) is 10,maxinumpoolsize (maximum number of threads) is 20, and there are 15 threads running in the thread pool, after a period of time, Where 3 of the threads are waiting for longer than the time specified by KeepAliveTime, the 3 threads are ended, and 12 threads are running in the thread pool. Unit: This is the time unit of KeepAliveTime, which can be nanoseconds, milliseconds, seconds, minutes, etc. WorkQueue:
Task queue. When the threads in the thread pool are running, and the number of tasks continues to increase, a container is required to accommodate these tasks, which is the task queue. This task queue is a blocking single-ended queue.
Threadfactory: Defines how to start a thread, can set the name of a thread, and can determine whether it is a background thread, etc. Handler: Reject task processor. A program that processes a task that continues to increase due to the number of threads and the capacity of the queue. The main parameters in the Ok,threadpoolexecutor are finished. Let's talk about the thread management process: first create a thread pool, then incrementally increase the threads to corepoolsize based on the number of tasks, and if there are still additional tasks at this point, place them in workqueue until Workqueue is full. Then continue to increase the number of threads in the pool (enhanced processing power) and eventually reach Maxinumpoolsize. What if there's a task to add at this point? This requires handler to dispose of, or discard new tasks, or reject new tasks, or squeeze out existing tasks. In the case of both the task queue and the thread pool are saturated, once the thread is waiting (the task is finished and no new task) is over KeepAliveTime, then it terminates, that is, the number of threads in the pool will gradually decrease until the number of corepoolsize is reached. This example in the book "151 recommendations for writing high-quality code to improve Java programs" is very image: OK, Let's take a look at how to put the thread task into the Task force: The Submit method of this class in Java.util.concurrent.AbstractExecutorService
PublicFuture<?>Submit (Runnable Task) {if(Task = =NULL)Throw NewNullPointerException (); Runnablefuture<Void> ftask = newtaskfor (Task,NULL); Execute (ftask);//Perform Tasks returnFtask; } /** * @throwsrejectedexecutionexception {@inheritDoc} * @throwsNullPointerException {@inheritDoc} */ Public<T> future<t>Submit (Runnable task, T result) {if(Task = =NULL)Throw NewNullPointerException (); Runnablefuture<T> Ftask =newtaskfor (task, result); Execute (ftask);//Perform Tasks returnFtask; } /** * @throwsrejectedexecutionexception {@inheritDoc} * @throwsNullPointerException {@inheritDoc} */ Public<T> future<t> Submit (callable<t>Task) { if(Task = =NULL)Throw NewNullPointerException (); Runnablefuture<T> Ftask =newtaskfor (Task); Execute (ftask);//Perform Tasks returnFtask; }
Here are three overloaded methods, corresponding to the runnable, the Runnable interface with the result, and the callable callback function. One of the newtaskfor is also an overloaded method, which through the layer of packaging, the Runnable interface is packaged into a suitable runnablefuture implementation class, the bottom-level implementation is as follows:
Public Futuretask (Runnable Runnable, V result) { this. Callable= executors.callable (Runnable, result); this. state = NEW; // ensure visibility of callable }
Public static <T> callable<t> callable (Runnable task, T result) { ifnull ) thrownew nullpointerexception (); return New Runnableadapter<t>(task, result); }
Static Final class Implements Callable<t> { final Runnable task; Final T result; Runnableadapter (Runnable task, T result) { this. Task = task; this. result = result; } Public T Call () { task.run (); return result; } }
The most important thing in submit is the Execute method, which is the focus of our analysis.
Execute method:
Public voidExecute (Runnable command) {if(Command = =NULL) Throw NewNullPointerException (); intc =Ctl.get (); if(Workercountof (c) < corepoolsize) {// if(Addworker (Command,true)) return; C=Ctl.get (); } if(IsRunning (c) &&workqueue.offer (command)) { intRecheck =Ctl.get (); if(! isrunning (Recheck) &&Remove (command)) reject (command); Else if(Workercountof (recheck) = = 0) Addworker (NULL,false); } Else if(!addworker (Command,false) ) reject (command); }
In this method is divided into three parts 1, if less than corepoolsize number of threads are running, then start a new thread and put the incoming runnable as the first task. It then checks the running state of the thread and the number of workers, prevents non-compliant tasks from being added to the thread 2, and if a task is successfully placed in the queue, we still need to check whether we should add the thread or stop it two times. So we re-check the thread state, whether we need to roll back the queue, or stop or start a new thread 3, if we can't add a queue task, but still add a task to the queue, and if the addition fails, use a deny policy to handle it. The main thing here is Addworker this method:
Try{W=NewWorker (Firsttask); FinalThread T =W.thread; if(t! =NULL) { FinalReentrantlock Mainlock = This. Mainlock; Mainlock.lock (); Try { //recheck while holding lock. //On threadfactory failure or if//shut down before lock acquired. intrs =runstateof (Ctl.get ()); if(Rs < SHUTDOWN | |(Rs= = SHUTDOWN && Firsttask = =NULL)) { if(T.isalive ())//PreCheck that T is startable Throw Newillegalthreadstateexception (); Workers.add (w); ints =workers.size (); if(S >largestpoolsize) Largestpoolsize=s; workeradded=true; } } finally{mainlock.unlock (); } if(workeradded) {T.start (); Workerstarted=true; } } } finally { if(!workerstarted) addworkerfailed (w); }
We create a thread in this method, noting that this thread is not our task thread, but a wrapped worker thread. So the Run method here is the run method in the worker class. The Execute method is a worker thread that starts with the worker class, executes our first task, and then the thread always gets the task from the task queue through the Gettask method before it resumes execution. This task queue is a blockingqueue, which is a blocking type, that is, if the queue element is 0, the wait state is maintained. Until a task is entered.
Multi-thread thread pool-meaning of each parameter-Ali interview topic