Interview analysis of the underlying thread pool

Thread pool class diagram

Executor ==> ExcutorService ==> AbstractExecutorService ==> ThreadPoolExecutor to analyze.

 

  • Above url inheritance hierarchy, the top level of the interface is the thread pool Executor, this interface has only one method void execute (Runnable command)
  • ExecutorService inheritance Executor, several major new method submit (Runnable (Callable)), shutDown, shutDownNow etc.
  • AbstractExecutorService implements several interfaces ExecutorService above method.
  • ThreadPoolExecutor inheritance AbstractExecutorService, to achieve some of the major methods execute thread pool (Runnable).

 

AbstractExecutorService

AbstractExecutorService achieved submit method, as follows:

submit(Callable task)方法

  1. public <T> Future<T> submit(Callable<T> task) {
  2. if (task == null) throw new NullPointerException();
  3. RunnableFuture<T> ftask = newTaskFor(task);
  4. execute(ftask);
  5. return ftask;
  6. }

newTaskFor(Callable callable)方法

  1. protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
  2. return new FutureTask<T>(callable);
  3. }

The above FutureTask realized RunnableFuture interfaces, RunnableFuture inherits the Runnable and Future interfaces. Void run only a Runnable interface method, Future interfaces cancel (boolean), V get (), V get (long timeout, TimeUnit unit), boolean isCancelled (), boolean isDone () method.

ThreadPoolExecutor

Then the above AbstractExecutorService.submit method calls to execute (ftask), this is to execute the ThreadPoolExecutor. Then we have to analyze in order to execute the method as a starting point.

execute

  1. public void execute(Runnable command) {
  2. if (command == null)
  3. throw new NullPointerException();
  4. int c = ctl.get();
  5. if (workerCountOf(c) < corePoolSize) {
  6. if (addWorker(command, true))
  7. return;
  8. c = ctl.get();
  9. }
  10. if (isRunning(c) && workQueue.offer(command)) {
  11. int recheck = ctl.get();
  12. if (! isRunning(recheck) && remove(command))
  13. reject(command);
  14. else if (workerCountOf(recheck) == 0)
  15. addWorker(null, false);
  16. }
  17. else if (!addWorker(command, false))
  18. reject(command);
  19. }
  • First, check the current number of worker threads is less than corePoolSize, if less than 10, add a worker to handle this task (commadn), add the task successfully is returned.
  • If the thread is still in the running state, and the task successfully added to the queue, the re-check the status of a thread pool, thread pool if the non-running, delete the job from the queue, the success of the call reject, refuse here to perform according to the policy; if the current number of worker threads is 0, then add a worker (addWorker (null, false), to note here, is not the same as the first parameter and the addWorker above)
  • If you add a worker fails to perform also reject method.

 

addWorker

 

  1. private boolean addWorker(Runnable firstTask, boolean core) {
  2. retry:
  3. for (;;) {
  4. int c = ctl.get();
  5. int rs = runStateOf(c);
  6.  
  7. // Check if queue empty only if necessary.
  8. if (rs >= SHUTDOWN &&
  9. ! (rs == SHUTDOWN &&
  10. firstTask == null &&
  11. ! workQueue.isEmpty()))
  12. return false;
  13.  
  14. for (;;) {
  15. int wc = workerCountOf(c);
  16. if (wc >= CAPACITY ||
  17. wc >= (core ? corePoolSize : maximumPoolSize))
  18. return false;
  19. if (compareAndIncrementWorkerCount(c))
  20. break retry;
  21. c = ctl.get(); // Re-read ctl
  22. if (runStateOf(c) != rs)
  23. continue retry;
  24. // else CAS failed due to workerCount change; retry inner loop
  25. }
  26. }
  27.  
  28. boolean workerStarted = false;
  29. boolean workerAdded = false;
  30. Worker w = null;
  31. try {
  32. w = new Worker(firstTask);
  33. final Thread t = w.thread;
  34. if (t != null) {
  35. final ReentrantLock mainLock = this.mainLock;
  36. mainLock.lock ();
  37. try {
  38. // Recheck while holding lock.
  39. // Back out on ThreadFactory failure or if
  40. // shut down before lock acquired.
  41. int rs = runStateOf(ctl.get());
  42.  
  43. if (rs < SHUTDOWN ||
  44. (rs == SHUTDOWN && firstTask == null)) {
  45. if (t.isAlive()) // precheck that t is startable
  46. throw new IllegalThreadStateException();
  47. workers.add(w);
  48. int s = workers.size();
  49. if (s > largestPoolSize)
  50. largestPoolSize = s;
  51. workerAdded = true;
  52. }
  53. } finally {
  54. mainLock.unlock ();
  55. }
  56. if (workerAdded) {
  57. t.start();
  58. workerStarted = true;
  59. }
  60. }
  61. } finally {
  62. if (! workerStarted)
  63. addWorkerFailed(w);
  64. }
  65. return workerStarted;
  66. }

(1) The decision logic is more complex, we first look at the

  1. if (rs >= SHUTDOWN &&
  2. ! (rs == SHUTDOWN &&
  3. firstTask == null &&
  4. ! workQueue.isEmpty()))
  5. return false;

If the current state is greater than SHUTDOWN, if the determination condition is apparently ture, direct returnfalse. (Well understood, the thread pool is closed certainly not to add a new worker a) If the current state of less than SHUTDOWN, if the judgment condition is false, then go down (thread pool RUNNING state, well understood) If the current state of equal SHUTDOWN: If the work queue firstTask equals null and has the task, it is judged if the condition is false, the code does not return, will continue to run down; is not equal to null, or if firstTask work queue is empty, it is determined that the condition is true, will return false (Ye Hao understand this, we know SHUTDOWN state, the thread pool is no longer accepting new tasks, but the task has been in the work queue or to complete the job. Therefore, if the first is equal to null, and the work queue has the task, but also continue to laid down. If the opposite, it will not go down) (2) to determine the current number of worker threads

  1. for (;;) {
  2. int wc = workerCountOf(c);
  3. if (wc >= CAPACITY ||
  4. wc >= (core ? corePoolSize : maximumPoolSize))
  5. return false;
  6. if (compareAndIncrementWorkerCount(c))
  7. break retry;
  8. c = ctl.get(); // Re-read ctl
  9. if (runStateOf(c) != rs)
  10. continue retry;
  11. // else CAS failed due to workerCount change; retry inner loop
  12. }

The current number of worker threads is no limit parameter exceeds the thread pool settings, the use of CAS to add a worker, and out of the outer for loop, continue to run down. Otherwise, it returns false, add the worker fails. (3) After completion of the above step 12, performs a new Worker (firstTask), Thread t = w.thread thread pool and check the state again, if valid, is added to the current working thread pool worker HashSet and executes t .start. This time before opening the sub-thread to perform the task.

Sub-thread run method

Step 3 above calls t.start, run method will open a sub-thread to run in Worker.

  1. public void run() {
  2. runWorker(this);
  3. }
  4.  
  5. final void runWorker(Worker w) {
  6. Thread wt = Thread.currentThread();
  7. Runnable task = w.firstTask;
  8. w.firstTask = null;
  9. w.unlock(); // allow interrupts
  10. boolean completedAbruptly = true;
  11. try {
  12. while (task != null || (task = getTask()) != null) {
  13. w.lock();
  14. // If pool is stopping, ensure thread is interrupted;
  15. // if not, ensure thread is not interrupted. This
  16. // requires a recheck in second case to deal with
  17. // shutdownNow race while clearing interrupt
  18. if ((runStateAtLeast(ctl.get(), STOP) ||
  19. (Thread.interrupted() &&
  20. runStateAtLeast(ctl.get(), STOP))) &&
  21. !wt.isInterrupted())
  22. wt.interrupt();
  23. try {
  24. beforeExecute(wt, task);
  25. Throwable thrown = null;
  26. try {
  27. task.run();
  28. }finally {
  29. afterExecute(task, thrown);
  30. }
  31. } finally {
  32. task = null;
  33. w.completedTasks++;
  34. w.unlock();
  35. }
  36. }
  37. completedAbruptly = false;
  38. } finally {
  39. processWorkerExit(w, completedAbruptly);
  40. }
  41. }

Above worker continuously through the getTask () method, the acquisition task from workQueue; if no task is acquired, processWorkerExit method is called.

getTask()

  1. private Runnable getTask() {
  2. boolean timedOut = false; // Did the last poll() time out?
  3. for (;;) {
  4. int c = ctl.get();
  5. int rs = runStateOf(c);
  6. // Check if queue empty only if necessary.
  7. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
  8. decrementWorkerCount();
  9. return null;
  10. }
  11. int wc = workerCountOf(c);
  12. // Are workers subject to culling?
  13. boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
  14. if ((wc > maximumPoolSize || (timed && timedOut))
  15. && (wc > 1 || workQueue.isEmpty())) {
  16. if (compareAndDecrementWorkerCount(c))
  17. return null;
  18. continue;
  19. }
  20. try {
  21. Runnable r = timed ?
  22. workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
  23. workQueue.take();
  24. if (r != null)
  25. return r;
  26. timedOut = true;
  27. } catch (InterruptedException retry) {
  28. timedOut = false;
  29. }
  30. }
  31. }

getTask is a method for infinite loop method, it first determines the current state of thread pool

  1. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
  2. decrementWorkerCount();
  3. return null;
  4. }

This judgment is also well understood, if rs == SHUTDOWN, workQueue is empty, obviously it should return null directly, and in advance of the work is reduced by one worker. (The getTask return null, runWorker method calls processWorkerExit remove current from the HashSet worker); if rs> greater than the SHUTDOWN (corresponding to the thread pool shutDownNow this method, the work queue waiting task is not executed); otherwise, the thread pool described in running, continue to run down. Then the maximum number of threads the thread pool is currently set, and whether to allow over time and the status line coreThread workQueue determines whether operation by a CAS number of threads together Save return null. Finally, we want to get attention at the following tasks from the work queue three head operations.

  1. Runnable r = timed ?
  2. workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
  3. workQueue.take();

If timed to ture (allowCoreThreadTimeOut set to true), and more than the waiting time has not yet achieved the task from the workQueue r = null, even at this time it may cause less than workerCount corePoolSize, the current worker may also be recovered. If timed false, the call blocking method to get the task from workQueue, newFixedThreadPool will always call the blocking method, so as to achieve without displaying the thread pool is closed, even if workQueue is empty, but also to maintain a fixed number of worker threads .

shutDown (shutDownNow) method

  1. public List<Runnable> shutdownNow() {
  2. List<Runnable> tasks;
  3. final ReentrantLock mainLock = this.mainLock;
  4. mainLock.lock ();
  5. try {
  6. checkShutdownAccess();
  7. // shutDwonNow as STOP, shutDown as SHUTDOWN
  8. advanceRunState(STOP);(advanceRunState(SHUTDOWN);)
  9. interruptWorkers();(interruptIdleWorkers)
  10. // shutDownNow special
  11. tasks = drainQueue();
  12. // shutDown special ScheduledThreadPoolExecutor callback
  13. onShutdown();
  14. } finally {
  15. mainLock.unlock ();
  16. }
  17. tryTerminate ();
  18. return tasks;
  19. }

shutDown and shutDownnNow difference method (code level):

  • shutDownNow:advanceRunState(STOP),interruptWorkers shutDown:advanceRunState(shutDown),interruptIdleWorkers
  • shutDown more a onShutdown (); ScheduledThreadPoolExecutor onShutDown the replication method.
  • shutDownNow methods work queue uncompleted task.
  • interruptIdleWorkers

interruptIdleWorkers与interruptWorkers

(1)shutDownNow

  1. private void interruptWorkers() {
  2. final ReentrantLock mainLock = this.mainLock;
  3. mainLock.lock ();
  4. try {
  5. for (Worker w : workers)
  6. w.interruptIfStarted();
  7. } finally {
  8. mainLock.unlock ();
  9. }
  10. }

Obviously, this is interrupted all the threads (2) shutDown

  1. private void interruptIdleWorkers(boolean onlyOne) {
  2. final ReentrantLock mainLock = this.mainLock;
  3. mainLock.lock ();
  4. try {
  5. for (Worker w : workers) {
  6. Thread t = w.thread;
  7. if (!t.isInterrupted() && w.tryLock()) {
  8. try {
  9. t.interrupt();
  10. } catch (SecurityException ignore) {
  11. } finally {
  12. w.unlock();
  13. }
  14. }
  15. if (onlyOne)
  16. break;
  17. }
  18. } finally {
  19. mainLock.unlock ();
  20. }
  21. }

Note onlyOne parameters, this is only on the inside call tryTerminate () method calls interruptIdleWorkers (true), other cases are interruptIdleWorkers (false), so for shutDown method, but also try to break all has not been interrupted thread. 3) tryTerminate tryTerminate method mentioned above in (2), this method will look at the next

  1. final void tryTerminate() {
  2. for (;;) {
  3. int c = ctl.get();
  4. if (isRunning(c) ||
  5. runStateAtLeast(c, TIDYING) ||
  6. (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
  7. return;
  8. if (workerCountOf(c) != 0) { // Eligible to terminate
  9. interruptIdleWorkers(ONLY_ONE);
  10. return;
  11. }
  12.  
  13. final ReentrantLock mainLock = this.mainLock;
  14. mainLock.lock ();
  15. try {
  16. if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
  17. try {
  18. terminated();
  19. } finally {
  20. ctl.set(ctlOf(TERMINATED, 0));
  21. termination.signalAll();
  22. }
  23. return;
  24. }
  25. } finally {
  26. mainLock.unlock ();
  27. }
  28. // else retry on failed CAS
  29. }
  30. }

As can be seen from the above code, empty, if the number of worker threads the thread pool state is SHUTDOWN, workQueue thread pool is 0 or the STOP state, the number of worker thread 0, thread pool will eventually have to TERMINATED state, and wakes up all because the call awaitTermination () method blocks in termination.awaitNanos (nanos) has not wake up the thread.

  1. public boolean awaitTermination(long timeout, TimeUnit unit)
  2. throws InterruptedException {
  3. long nanos = unit.toNanos(timeout);
  4. final ReentrantLock mainLock = this.mainLock;
  5. mainLock.lock ();
  6. try {
  7. for (;;) {
  8. if (runStateAtLeast(ctl.get(), TERMINATED))
  9. return true;
  10. if (nanos <= 0)
  11. return false;
  12. nanos = termination.awaitNanos(nanos);
  13. }
  14. } finally {
  15. mainLock.unlock ();
  16. }
  17. }

TryTerminate above method, addWorkerFailed (), processWorkerExit (), shutDown (), shutDownNow (), remove (Runnable task) method will be called to.

5 states to explain the thread pool

The above mentioned frequently running state of the thread pool, a brief explanation here.

  1. private static final int RUNNING = -1 << COUNT_BITS;
  2. private static final int SHUTDOWN = 0 << COUNT_BITS;
  3. private static final int STOP = 1 << COUNT_BITS;
  4. private static final int TIDYING = 2 << COUNT_BITS;
  5. private static final int TERMINATED = 3 << COUNT_BITS;

The definition of states

  • RUNNING: new tasks, processing tasks in workQueue.
  • SHUTDOWN: do not accept new tasks, but will continue to complete the task workQueue
  • STOP: do not accept the new task does not deal workQueue unfinished task, try to break all running tasks
  • TIDYING: all tasks have been completed, the number of worker threads is 0, the thread pool state will become TIDYING will call terminated () method.
  • TERMINATED: terminated () method has been completed

Five states conversion

  • RUNNING -> SHUTDOWN: call shutdown () method, perhaps implicit in the finalize () method
  • (RUNNING or SHUTDOWN) -> STOP: call shutdownNow () method
  • SHUTDOWN -> TIDYING: workQueue and pool are empty
  • STOP -> TIDYING: pool is empty
  • TIDYING -> TERMINATED: terminated()方法完成

Source: http://yeming.me/2016/05/07/threadPool1/

Published 277 original articles · won praise 65 · views 380 000 +

Guess you like

Origin blog.csdn.net/ailiandeziwei/article/details/104749570