Thread pool features:
Reducing resources: Reducing the wastage of thread creation and destruction by reusing created threads
Enhance efficiency: When the task is completed, no waiting is required, and it is executed immediately.
Convenient Management: Unified Distribution, Tuning and Monitoring
Thread pool creation:
1.CachedThreadPool: Create a cacheable thread pool to reclaim idle threads flexibly
public class ThreadPoolDemo { public static void main(String[] args) { ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int temp = i; newCachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "->" + temp); } }); } } }
After printing, it can be found that the same thread has been reused, and the theoretical size of thread pool is infinite.
2. Fixed ThreadPool: Create a fixed-length thread pool to control the maximum concurrency of threads, and the threads that exceed it will wait in the queue
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
The rest of the code remains unchanged, and printing reveals that five threads are reusing
3. Scheduled ThreadPool: Support for regular task execution
public class ThreadPoolDemo { public static void main(String[] args) { ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5); for (int i = 0; i < 10; i++) { final int temp = i; newScheduledThreadPool.schedule(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "->" + temp); } }, 3, TimeUnit.SECONDS); } } }
Watch Printing: Wait 3s for Printing
4. Single ThreadExecutor: Single Thread
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
Print discovery calls only one thread
The principle of thread pool:
All four approaches follow the construction method of ThreadPool Executor:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
corePoolSize: Maximum Number of Core Threads: Number of Actual Threads
Maximum PoolSize: Maximum number of threads: Maximum number of threads created in the thread pool
If the number of threads in the current thread pool is less than corePoolSize, a thread is created to perform each task.
If the number of threads in the current thread pool >= corePoolSize, each task will try to add it to the task cache queue. If it succeeds, the task will wait for the idle thread to take it out and execute. If the addition fails (generally speaking, the task cache queue is full), it will try to create new threads to execute the task.
If the queue is full, a new thread is created if the number of bus threads is not greater than maximum PoolSize.
If the number of threads in the current thread pool reaches maximum PoolSize, the task rejection strategy will be adopted for processing.
If the number of threads in the thread pool is larger than corePoolSize, if the idle time of a thread exceeds keepAliveTime, the thread will terminate until the number of threads in the thread pool is not greater than corePoolSize; if the thread in the core pool is allowed to set the lifetime, the thread in the core pool will also terminate if the idle time of the thread exceeds keepAliveTime.
Write a piece of code to understand:
public class MyThreadPool { public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( //Number of core threads is 1 1, //Maximum number of threads is 2 2, //Keep alive for 0 milliseconds, meaning no recycling after use 0L, TimeUnit.MILLISECONDS, //Blocking queue new LinkedBlockingQueue<Runnable>(3)); threadPoolExecutor.execute(new TaskThread("task1")); threadPoolExecutor.execute(new TaskThread("task2")); threadPoolExecutor.execute(new TaskThread("task3")); } } class TaskThread implements Runnable { private String threadName; TaskThread(String threadName) { this.threadName = threadName; } @Override public void run() { System.out.println(threadName); } }
When task1 is submitted, a thread is created to execute directly, and the number of core threads is equal to the number of threads that exist.
When task2 is submitted, it is stored in the queue cache, where the number of threads is equal to the maximum number of threads
When task3 was submitted, it was also cached in the queue, and my blocked queue size was 3, and now only two are stored, so there is no error.
In theory, submitting to task5 will not cause an error, with a maximum number of threads 2 + queue size 3 = 5; however, submitting task6 will cause an error.
Another point: If submitted to task4, print the thread name: only thread 1 is invoked, and thread 2 is not invoked.
Because: task1 submits, task2, task3, task4 are stored in the queue, which just reaches the maximum value of the queue, so instead of creating new threads, thread 1 performs these three tasks in turn.
Reasonable configuration of thread pool:
Principle:
CPU-intensive: Tasks require a lot of computation, but without blocking, the CPU can run at full speed.
IO-intensive: Tasks require a lot of IO operations, resulting in blocking
The fewer CPU-intensive threads, the better to configure them as CPU core numbers
IO-intensive threads should be as many as possible, preferably configured as CPU core number*2