The previous article covered some simple usage of thread pools, and this article takes you a step closer to understanding how thread pools work and how they work from a source perspective.

First, let’s review how to start a thread using a thread pool

Private static void createThreadByThreadPoolExecutor () {ThreadPoolExecutor executor = new ThreadPoolExecutor (5, 5, 0 l, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); for (int i = 0; i < 10; i++) { MyThread myThread = new MyThread(); executor.execute(myThread); }

As you can see, there’s nothing special about it, except for the code to build the pool of threads. The only final thing you need is the executor.execute(myThread) line of code.

The preparatory work

The state of threads and processes was mentioned in the first article in the multithreading series. Thread pools also have state, as follows:

  • Running: Allows you to accept new tasks and process tasks in the queue
  • Shutdown: No new tasks are accepted, but tasks in the queue will still be processed
  • Stop: Do not accept new tasks, do not process tasks in the queue, and the middle end of the running tasks
  • Tidying:All tasks have been terminated and the number of worker threads is zero. Threads in this state will callterminated()function
  • Terminated:terminated()This state is entered when the function is called

First, you need to know four concepts: Worker, Worker, workQueue, and task.

  • Have a important class in the thread pool, that is the Worker, you can see its implement the Runnable interface (is actually working thread), inherited AbstractQueuedSynchronizer class (commonly known as AQS, it is very important in a multithreaded class)

  • workersisWorkerA set of,private final HashSet<Worker> workers = new HashSet<Worker>();
  • task: you need toTasks Performed, that is,execute()Parameter in theRunnableinterface
  • A workQueue is a workQueue in the thread pool constructor from the previous article that stores the tasks that need to be executed. A queue is a class that implements the BlockingQueue interface, which has the following implementations

execute()

This method passes a class that implements the Runnable interface as a command

After encountering a new task

  1. If the number of worker threads is < the number of core threads, then add 1 worker
  2. If the thread pool is working properly and the work queue is capable of adding tasks, a second round of judgment is required

    1. If the thread pool is out of order for some reason and the task is successfully removed from the work queue, then the reject policy is adopted
    2. If the number of worker threads is zero, then a new thread (and the non-core thread created here) is needed to execute the task. If the new worker’s firstTask is this task, the task will be repeated twice.
    3. In other cases, which is the normal case, nothing is done, because the main purpose is to put the work in the work queue
  3. If the work queue is full, it is necessary to judge whether a non-core thread can be added successfully. If the number of non-core threads cannot be satisfied, the thread pool is really unable to handle it, so the rejection policy will also be invoked.

Note: Core and non-core threads are just semantic terms,
There is no essential difference

addworker()

AddWorker checks whether a new thread can be added based on the current pool state and a given threshold (core or maximum). The second parameter determines whether a core thread is created. If true, it is a core thread and if not, it is a non-core thread. The comments in the source code are as follows

So what does the code look like

  1. The first step in the loop is a loop, the main purpose of this loop is to verify that the thread pool state is normal. If the state of the thread pool is greater than SHUTDOWN and the thread pool is TERMINATED, then create worker and return to fasle directly. When the thread pool is SHUTDOWN, it must be checked again:

    1. If the incoming task is not empty, then there is no need to add the worker. The bank is off duty, and the current customer will not be processed after the bank is off duty. Otherwise, it will not be able to get off duty all the time.
    2. The incoming task is empty, but the task force is listed as empty. Also, there is no need to add worker, because there is no task in the bank and it is time to get off work again. Except for the above 3 cases that return false, no other cases will do anything. Move on.
  2. If the number of worker threads exceeds the boundary, such as the capacity, the number of core threads or the maximum number of threads, there is no need to add worker. The bank is really unable to handle new customers. When the number of worker threads is normal, increase the number of worker threads through CAS, and exit the outermost loop if the increase is successful. If the increase in the number of worker threads fails, then another thread has increased the number. If the running state of the thread pool has changed at this time, the outer loop is repeated. Otherwise, it spins until the number of worker threads is successfully increased.
  3. Create a new Worker with the incoming FirstTask, and then get the thread in the Worker. If the thread is null, the logic to add the Worker fails directly; otherwise, the logic is normal.
  4. Let’s start with normal logic, get the lock, and start getting the state of the thread pool again

    1. If the thread is running normally, or if the firstTask is still null in the closed state, then if the thread isaliveIf the thread is already open, throw an exception.
    2. The normal process is to add a new worker element to the wokers collection, adjust the thread pool maximum, and set the WorkerAdded flag to true.
    3. Focus onWhen WorkerAdded is true, start the worker thread in the codet.start()
  5. Look atThe logic of failureIn the third point, a Worker object is constructed. When obtaining a thread, it is possible to obtain an empty thread. In fact, this is to increase Worker failure and return false, which will trigger the logic of execution failure before returning falseaddWorkerFailed()Logic. The whole logic is relatively simple, we will not spend any space to elaborate.

At this point, the entire flow of addWorker() is fairly clear. One thing to note is that in line 2, the word “retry” looks like a keyword, but it’s not. It’s just something like a flag bit, either retry or ABC.

Usually used with a for loop, with a continue and a break to achieve the goto effect. Such as the continue retry; That is to jump to the first, the outermost for loop, break retry; You exit the whole cycle. Let me give you the simplest example you can get the idea.

public class RetryExample { public static void main(String[] args) { abc: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (j == 2) { continue abc; } if (i == 8) { break abc; } System.out.println("i: " + i + ", j: " + j); }}}}

The output is shown in the figure below

It is not easy to create, if you have help, welcome thumb up, collection and share!

The following is a personal public number, interested can pay attention to, perhaps is your treasure public number oh, basic 2,3 days 1 more technical articles!!