What is a thread pool? Why thread pools?
Thread pool: A thread usage pattern. Too many lines will bring scheduling overhead, which will affect cache locality and overall performance. A thread pool maintains multiple threads, waiting for the supervisor to assign tasks that can be executed concurrently. This avoids the cost of creating and destroying threads while working on short-duration tasks. Thread pools not only ensure full utilization of the kernel, but also prevent overscheduling.
Advantage:
- Reduce resource consumption. Reduce resource consumption for thread creation and destruction;
- Improve response speed. For example, a thread is created at T1, executed at T2, and destroyed at T3, leaving out T1 and T3
- Improve thread manageability.
How to implement a thread pool
According to the thread pool concept, if you want to create your own thread pool, the following conditions should be met.
- A container for holding threads. Because threads have to be created in the pool and can be held, we need a container to hold our threads.
- Accept external assignments. Threads should also be able to accept external tasks, freeze them and run them.
- A container for storing tasks. Some tasks may be too late to be executed, so you need to save tasks that are too late to be executed through the container.
Based on the above conditions and what we learned about concurrent programming, let’s first try to write a thread pool by hand
Code:
import java.util.LinkedList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * @Auther: BlackKingW * @Date: 2019/4/14 12:09 * @Description: */ public class MyThreadPool {private static int WORK_NUM = 5; Private static int TASK_COUNT = 100; private static int TASK_COUNT = 100; Private workThreads [] workThreads; Private final BlockingQueue<Runnable> taskQueue; private final int worker_num; Public MyThreadPool() {this(WORK_NUM,TASK_COUNT); Public MyThreadPool(int worker_num,int taskCount) {if (worker_num<=0) worker_num = WORK_NUM; if(taskCount<=0) taskCount = TASK_COUNT; this.worker_num = worker_num; taskQueue = new ArrayBlockingQueue<>(taskCount); workThreads = new WorkThread[worker_num]; for(int i=0; i<worker_num; i++) { workThreads[i] = new WorkThread(); workThreads[i].start(); }} public void execute(Runnable task) {try {taskQueue. Put (task); } catch (InterruptedException e) { e.printStackTrace(); Public void destroy() {// The worker thread stops working. Println (" Ready close pool.....") ); for(int i=0; i<worker_num; i++) { workThreads[i].stopWorker(); workThreads[i] = null; //help gc } taskQueue.clear(); } // Override toString to return thread pool information: Number of working threads and completed tasks @Override public String toString() {return "WorkThread number:" + worker_num + "Wait task number:" + taskQueue.size(); } /** * private class extends Thread{@override public void run(){Runnable r = null; try { while (! IsInterrupted ()) {// Listen to the blocking queue and if there is a task, execute the corresponding task r = taskqueue.take (); if(r! =null) { System.out.println(getId()+" ready exec :"+r); r.run(); } r = null; //help gc; }} catch (Exception e) {// TODO: handle Exception}} public void stopWorker() {interrupt(); }}}Copy the code
In this thread pool, we created workThreads to hold running threads and BlockingQueue taskQueue to hold our taskQueue.
1. Define the WorkThread class that represents the executing thread and listens for blocking queue tasks.
2. Create the builder function, we initialize the thread pool and start all the worker threads. WorkThreads is used to hold running threads, and BlockingQueue taskQueue is used to hold our taskQueue
3. Create the execute task method to submit our task.
Create a thread pool destroy method, which destroys the thread pool.
Then we write the test class
import java.util.Random; /** * @Auther: BlackKingW * @Date: 2019/4/14 12:09 * @Description: */ public class TestMyThreadPool {public static void main(String[] args) throws InterruptedException {// Create a thread pool of 3 threads MyThreadPool t = new MyThreadPool(3,0); t.execute(new MyTask("testA")); t.execute(new MyTask("testB")); t.execute(new MyTask("testC")); t.execute(new MyTask("testD")); t.execute(new MyTask("testE")); System.out.println(t); Thread.sleep(10000); t.destroy(); // Destory system.out.println (t); } // static class MyTask implements Runnable {private String name; private Random r = new Random(); public MyTask(String name) { this.name = name; } public String getName() { return name; } @override public void run() {// Execute the task try {thread.sleep (r.nextint (1000)+2000); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getId()+" sleep InterruptedException:" +Thread.currentThread().isInterrupted()); } system.out. println(" task "+ name +" done "); }}}Copy the code
The ability to execute as thread pools.
We simply wrote a thread pool manually, but what are the problems with the above thread pool?
1, start time will be all threads started, if a long time does not consume resources.
2. An error with no task saturation.
3. No task timeout mechanism, etc.
Thread pools in JDK and how they work
Now that we have an overview of a thread pool mechanism, let’s take a look at how it is implemented in the JDK.
Create a thread pool
In JAVA, ThreadPoolExecutor is the parent of all thread pool implementations. The class structure is shown below.
Its constructor takes the following arguments
parameter | meaning |
int corePoolSize | The number of core threads in the thread pool, < corePoolSize, will create a new thread, = corePoolSize, and the task will be saved to BlockingQueue, The prestartAllCoreThreads () method will start the corePoolSize number of threads at once. |
int maximumPoolSize | Maximum number of threads allowed. If BlockingQueue is also full and the thread count is < maximumPoolSize, a new thread will be created |
long keepAliveTime | This parameter is only useful if the number of threads > corePoolSize |
TimeUnit unit | The unit value of survival time |
BlockingQueue workQueue | Save the blocking queue for the task |
ThreadFactory threadFactory | Create a factory for threads, giving names to new threads |
RejectedExecutionHandler handler | AbortPolicy: Directly throws an exception. Default. DiscardOldestPolicy: Discards the oldest task in the blocking queue and the most advanced task in the queue. DiscardPolicy: The current task simply discards its saturation policy by implementing the RejectedExecutionHandler interface |
Submit a task
Execute (Runnable Command) does not need to return
Future Submit (Callable Task) requires a return value
Closing the thread pool
shutdown(),shutdownNow();
ShutdownNow (): Sets the state of the thread pool. It also tries to stop threads that are running or suspending tasks
Shutdown () sets the state of the thread pool and interrupts only all threads that are not executing
2. How thread pools work
1. If the number of worker threads is smaller than the number of core threads, create a worker thread
2. If the number of worker threads is equal to or greater than the number of core threads, the task is submitted to the blocking queue
3. If the blocking queue is also full, but the number of threads is less than the maximum number, a new thread is created
4. If the new thread is also full, the task saturation policy is implemented.
The source code is as follows
3. How to configure thread pools properly
Depending on the nature of the task: computational intensive (CPU), IO intensive, hybrid
Computationally intensive: such as encryption, large number decomposition, re… Etc.
(Cpu cores = runtime.getruntime ().availableprocessors ();)
IO intensive: reading files, database connection, network communication,
Recommended: the number of threads is appropriately large, the number of Cpu cores of the machine *2,
Mixed type: try to split, if IO intensive far computation-intensive, split meaning is not big.
When choosing blocking queues, you should use bounded queues, as unbounded queues may cause memory overflow
4. Predefined thread pools
In Java, five thread pools are reserved for us
1, FixedThreadPool
Create thread pools with a fixed number of threads, suitable for heavily loaded servers, using unbounded queues
2, SingleThreadExecutor
Create a thread pool for a single thread, suitable for tasks that need to be executed sequentially, without multiple threads, using an unbounded queue
3, CachedThreadPool
SynchronousQueue, which creates new threads as needed, is used for programs that perform many short-term asynchronous tasks
WorkStealingPool (after JDK7)
Working hashing thread pools, implemented by ForkJoinPool. Thread pools suitable for large task decomposition.
5, ScheduledThreadPoolExecutor
Pool of threads that need to perform periodic tasks on a regular basis. There are two implementations
NewSingleThreadScheduledExecutor: contains only a single thread, need only a single thread execution cycle task, guarantee the order of various missions
The newScheduledThreadPool can contain multiple threads that perform periodic tasks, appropriately controlling the number of background threads
Method description:
Schedule: The task is executed only once and can be executed in a delay
ScheduleAtFixedRate: submits tasks at a fixed interval
ScheduleWithFixedDelay: indicates that tasks are submitted for execution at a fixed delay interval
Generally, if the concurrency is high in the business, the above thread pool is not recommended to use, because they use unbounded queue, when the task is large, it may cause memory overflow. It is generally correct to create using ThreadPoolExecutor directly, or to customize to your own needs.
This chapter focuses on the creation, use and operation of thread pools.