In-depth analysis of java thread pool implementation principles, in-depth analysis of java Thread Pool

Source: Internet
Author: User

In-depth analysis of java thread pool implementation principles, in-depth analysis of java Thread Pool

Preface

A thread is a scarce resource. If it is created without limit, it will not only consume system resources, but also reduce system stability. It is reasonable to use the thread pool to uniformly allocate, optimize, and monitor threads, benefits:

1. reduce resource consumption;

2. improve response speed;

3. Improve the manageability of threads.

The Executor framework introduced in Java1.5 decouples the submission and execution of tasks. You only need to define the tasks and submit them to the thread pool, you don't have to worry about how the task is executed, which thread is executed, and when it is executed.

Demo

1. Executors. newFixedThreadPool (10) initializes a thread pool executor that contains 10 threads;

2. Submit 20 tasks using the executor.exe cute method, and print the current thread name for each task;

3. The Executor framework manages the lifecycles of the threads responsible for task execution;

ThreadPoolExecutor

Executors is a factory class of the java thread pool. It can be used to quickly initialize a thread pool meeting business needs. For example, the Executors. newFixedThreadPool method can generate a thread pool with a fixed number of threads.

Essentially, a ThreadPoolExecutor object is initialized using different parameters. The specific parameters are described as follows:

CorePoolSize

Number of core threads in the thread pool. When a task is submitted, the thread pool creates a new thread to execute the task until the current number of threads is equal to corePoolSize. If the current number of threads is corePoolSize, the tasks that continue to be submitted are saved to the blocking queue and are waiting for execution. If the thread pool's prestartAllCoreThreads () method is executed, the thread pool will create and start all core threads in advance.

MaximumPoolSize

The maximum number of threads allowed in the thread pool. If the current blocking queue is full and the task continues to be submitted, a new thread is created to execute the task, provided that the current number of threads is smaller than maximumPoolSize;

KeepAliveTime

The alive time when the thread is idle, that is, the time when the thread continues to survive when no task is executed. By default, this parameter is only useful when the number of threads is greater than corePoolSize;

Unit

The unit of keepAliveTime;

WorkQueue

The blocking queue is used to save the tasks waiting for execution, and the task must implement the Runable interface. The following blocking queue is provided in JDK:

1. ArrayBlockingQueue: A Bounded blocking Queue Based on an array structure. tasks are sorted by FIFO;

2. Blocked blockingquene: a blocking Queue Based on the linked list structure. tasks are sorted by FIFO, And the throughput is usually higher than that of ArrayBlockingQuene;

3. SynchronousQuene: a blocking queue that does not store elements. Each insert operation must wait until another thread calls the remove operation. Otherwise, the insert operation is always in the blocking state, and the throughput is usually higher than LinkedBlockingQuene;

4. priorityBlockingQuene: an unbounded blocking queue with a priority;

ThreadFactory

The factory that creates a thread. With the custom thread factory, you can set a thread name with recognition for each newly created thread.

Handler

Thread Pool saturation policy. When the blocking queue is full and there is no idle working thread, if you continue to submit the task, you must adopt a policy to process the task, the thread pool provides four policies:

1. AbortPolicy: throws an exception directly. The default policy is used;

2. CallerRunsPolicy: Use the caller's thread to execute the task;

3. DiscardOldestPolicy: discard the top tasks in the blocking queue and execute the current task;

4. DiscardPolicy: directly discards the task;

Of course, you can also implement the RejectedExecutionHandler interface based on application scenarios and customize saturation policies, such as logging or tasks that cannot be processed by persistent storage.

Exectors

The Exectors factory class provides the initialization interface of the thread pool, mainly including the following:

NewFixedThreadPool

Initialize a thread pool with the specified number of threads, in which corePoolSize = maximumPoolSize uses the blocked blockingquene as the blocking queue. However, when the thread pool does not have executable tasks, the thread will not be released.

NewCachedThreadPool

1. initialize a thread pool that can cache threads. The default cache is 60 s. The number of threads in the thread pool can reach Integer. MAX_VALUE, that is, 2147483647. Internally, SynchronousQueue is used as the blocking queue;

2. Unlike the thread pool created by newFixedThreadPool, when newCachedThreadPool does not run a task, when the thread's idle time exceeds keepAliveTime, the thread resources are automatically released. When a new task is submitted, if there is no idle thread, creating a new thread to execute the task will lead to a certain amount of system overhead;

Therefore, when using this thread pool, you must control the number of concurrent tasks. Otherwise, creating a large number of threads may cause serious performance problems.

(Java high-level programmer) study and exchange QQ group: 478052716 when you are learning Java or encounter any problems at work, you can ask questions from the group. Alibaba Java senior cool livestream will explain knowledge points and share knowledge, years of work experience in organizing and summarizing, with everyone comprehensively and scientifically establishing their own technical system and technical cognition! You can add a group to ask for a link to the classroom. Note: It is free of charge and has no development experience! Do not like it!

NewSingleThreadExecutor

There is only one thread in the initialization thread pool. If the thread ends abnormally, a new thread will be created to continue executing the task. The only thread can ensure the sequential execution of the submitted tasks, internal use of LinkedBlockingQueue as the blocking queue.

NewScheduledThreadPool

The initialized thread pool can periodically execute submitted tasks within a specified period of time. In actual business scenarios, it can be used to regularly synchronize data.

Implementation Principle

In addition to the internal implementation of newScheduledThreadPool, several other thread pools are implemented based on the ThreadPoolExecutor class.

Internal Thread Pool status

Among them, the AtomicInteger variable ctl is very powerful: The 29 bit lower represents the number of threads in the thread pool, and the 3 bit higher represents the running status of the thread pool:

1. RUNNING:-1 <COUNT_BITS, that is, the 3-bit high value is 111. The thread pool in this status receives new tasks and processes tasks in the blocked queue;

2. SHUTDOWN: 0 <COUNT_BITS, that is, the 3-bit high value is 000. The thread pool in this status will not receive new tasks, but will process tasks in the blocked queue;

3. STOP: 1 <COUNT_BITS, that is, the 3-bit high value is 001. The thread in this status does not receive new tasks or process tasks in the blocked queue, it also interrupts running tasks;

4. TIDYING: 2 <COUNT_BITS, that is, the 3-digit height is 010;

5. TERMINATED: 3 <COUNT_BITS, that is, the 3-bit height is 011;

Task submission

The thread pool framework provides two ways to submit tasks and select different methods based on different business needs.

Executor.exe cute ()

The Runnable interface must be implemented for tasks submitted using the executor.exe cute () method. The returned values cannot be obtained for tasks submitted in this method, so it is impossible to determine whether the task is successfully executed.

ExecutorService. submit ()

Tasks submitted using the ExecutorService. submit () method can obtain the returned values after the task is executed.

Task execution

When a task is submitted to the thread pool, how does the thread pool handle the task?

Execute implementation

The specific execution process is as follows:

1. The workerCountOf method obtains the current number of threads in the thread pool based on the low 29-bit ctl. If the number of threads is smaller than corePoolSize, The addWorker method is executed to create a new thread to execute the task; otherwise, perform step 2 );

2. If the thread pool is in the RUNNING state and the submitted task is successfully put into the blocking queue, perform step (3); otherwise, perform step (4 );

3. Check the status of the thread pool again. If the thread pool does not have RUNNING and the task is successfully deleted from the blocked queue, the reject method is executed to process the task;

4. Execute the addWorker method to create a new thread to execute the task. If the addWoker fails to execute the task, execute the reject method to process the task;

AddWorker implementation

The implementation of the method execute shows that addWorker is mainly responsible for creating new threads and executing tasks. The code implementation is as follows:

This is only the first half of the implementation of the addWoker method:

1. determine the status of the thread pool. If the status value of the thread pool is greater than or equal to SHUTDOWN, the submitted tasks will not be processed and will be directly returned;

2. Determine whether the thread to be created is the core thread through the core parameter. If the core is true and the number of threads is smaller than corePoolSize, the system will jump out of the loop and start to create a new thread, the specific implementation is as follows:

The Worker thread of the thread pool is implemented through the Woker class. With the ReentrantLock guarantee, the Woker instance is inserted into the HashSet and the thread in the Woker is started. The Worker class is designed as follows:

1. inherits the AQS class to conveniently abort a work thread;

2. Implemented the Runnable interface, which can be executed as a task in the work thread;

3. The currently submitted task firstTask is used as a parameter to input the Worker constructor;

From the implementation of the Woker class constructor, we can find that when the thread factory creates a thread, the Woker instance itself this is passed as a parameter. When the start method is executed to start the thread, the essence is to execute the Worker runWorker method.

RunWorker implementation

The runWorker method is the core of the thread pool:

1. After the thread starts, use the unlock method to release the lock and set the AQS state to 0, indicating that the operation is interrupted;

2. Obtain the firstTask of the first task and execute the run method of the task. However, the lock operation will be performed before the task is executed, and the lock will be released after the task is executed;

3. Before and after the task is executed, you can customize the beforeExecute and afterExecute methods according to the business scenario;

4. After firstTask is executed, use the getTask method to obtain the waiting tasks from the blocked queue. If there are no tasks in the queue, the getTask method will be blocked and suspended without occupying cpu resources;

GetTask implementation

The entire getTask operation is completed in spin:

1. workQueue. take: If the blocking queue is empty, the current thread will be suspended for waiting. When a task is added to the queue, the thread will be awakened, and the take method will return and execute the task;

2. workQueue. poll: If no task is blocked in the queue during keepAliveTime, null is returned;

Therefore, threads in the thread pool can always execute tasks submitted by users.

Future and Callable implementation

Tasks submitted using the ExecutorService. submit () method can obtain the returned values after the task is executed.

In actual business scenarios, Future and Callable appear in pairs. Callable is responsible for generating results, and Future is responsible for obtaining results.

1. The Callable interface is similar to Runnable, but Runnable does not return a value.

2. In addition to the normal results returned by the Callable task, if an exception occurs, the Callable task will also be returned, that is, the Future can get various results of the asynchronous execution task;

3. The Future. get method will cause the main thread to block until the Callable task is completed;

Submit implementation

The Callable task submitted using the submit method is encapsulated into a FutureTask object.

FutureTask

1. FutureTask has different States in different stages and is initialized to NEW;

2、futuretaskclass the runnableinterface is implemented. In this example, the executor.exe cute method can be used to submit the FutureTask to the thread pool and wait for execution. The final execution is the run method of the FutureTask;

FutureTask. get implementation

The awaitDone method is used internally to block the main thread. The specific implementation is as follows:

1. If the main thread is interrupted, an exception is thrown;

2. determine the current state of the FutureTask. If the value is greater than COMPLETING, it indicates that the task has been completed, and the result is returned directly;

3. If the current state is equal to COMPLETING, it indicates that the task has been completed. At this time, the main thread only needs to use the yield method to make the cpu resources available and wait until the state changes to NORMAL;

4. encapsulate the current thread through the WaitNode class and add it to the waiters linked list through UNSAFE;

5. Finally, the thread is suspended through the LockSupport park or parkNanos;

FutureTask. run implementation

The FutureTask. run method is executed in the thread pool, not the main thread.

1. call the method for executing the Callable task;

2. If the call operation is successful, the set method is used to save the result;

3. If an exception occurs during call execution, an exception is saved through setException;

Set

SetException

In the set and setException methods, the status of FutureTask is modified through UnSAFE, and the finishCompletion method is executed to notify the main thread that the task has been completed;

FinishCompletion

1. When the get method of the FutureTask class is executed, the main thread is encapsulated into a WaitNode node and saved in the waiters linked list;

2. After the FutureTask task is executed, set the value of waiters through UNSAFE and wake up the main thread through the LockSupport class unpark;

 

Join the study exchange group 569772982 to learn and exchange.

Related Article

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.