-
Why do we have thread pools?
If there were no thread pool, we could perform 100 tasks like this:
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run(a) {
System.out.println("On a mission");
}
}).start();
}
Copy the code
Every time we execute a task, we need to create a thread. The frequent creation and destruction of the thread consumes very much performance. Can we find a place to save the thread after execution and wait for the next task to execute? This solves the problem of wasting performance by creating and destroying too many threads.
-
How to use thread pool?
Java already has thread pools for us, but many people just give up when they see so many parameters to pass…
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Copy the code
It doesn’t matter, Java has already sealed up four thread pool factories for us, so we can just use them instead of passing a bunch of arguments:
Create a cache of thread pool ExecutorService ExecutorService = Executors. NewCachedThreadPool (); Create a fixed number of thread pool ExecutorService ExecutorService = Executors. NewFixedThreadPool (5); Create a single thread pool ExecutorService ExecutorService = Executors. NewSingleThreadExecutor (); Create timing scheduling thread pool ExecutorService ExecutorService = Executors. NewScheduledThreadPool (5);
Copy the code
But… There are four which one should we use? Which is more suitable for us to use? Let’s just click on one and see the source code:
public static ExecutorService newCachedThreadPool(a) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Copy the code
ThreadPoolExecutor is still called internally and a lot of parameters are passed, so we still need to understand what these parameters mean, otherwise messing with the thread pool could be inefficient.
Note: The Alibaba Java Development Manual explicitly prohibits using Executors to create thread pools.
* Disable the thread pool creation by using Executors. * Disable the thread pool by using ThreadPoolExecutor. * Disable the thread pool by using Executors. The disadvantages of the thread pool objects returned by Executors are as follows: FixedThreadPool and SingleThreadPool: The allowed request queue length is Integer.MAX_VALUE, so a large number of requests may accumulate and result in OOM. CachedThreadPool and ScheduledThreadPool: The number of threads allowed to be created is integer. MAX_VALUE, which may create a large number of threads, resulting in OOM.Copy the code
-
ThreadPoolExecutor parameters
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Copy the code
parameter | use |
---|---|
corePoolSize | Core threads |
maximumPoolSize | Maximum number of threads |
keepAliveTime | Number of non-core threads the number of threads that can live without a task |
unit | Unit of survival time |
workQueue | Task blocking queue |
threadFactory | Thread factory |
handler | Rejection policies |
How’s that? Is it still confusing? 😏
-
Take a chestnut
We have to go to the bank to withdraw money. The bank only has five Windows at most, but only three are open normally, so two of them are always suspended. If there are too many people handling business, we need to wait in the waiting area.If we have five users to handle business, only three Windows can handle business at the same time, so the remaining two people need to wait in the waiting area
If we have 7 users to handle business, only 3 Windows can handle business at the same time, and the waiting area can only accommodate 3 people at most, with one more person. At this time, the bank manager sees that there are too many people today, so he can only call the employee on leave at window 4 to call him back to work
If we come to 9 users to handle business, even if window 5 is also opened at the same time, it still cannot accommodate all users. The bank manager can only solve this part of the extra users according to the situation. He may drive the extra users out, or he may help this user handle business, or it may be another situation
Now the business is almost finished, window 4 and window 5 are idle for a while, the bank manager let window 4 and window 5 off work, after all, they were on holiday today.
-
Banks plug into the thread pool
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Copy the code
Core thread: corePoolSize
Windows 1, 2, and 3 are core threads that will not be destroyed whether they have a task or not.
Maximum thread: maximumPoolSize
Windows 4 and 5 are the difference between the maximum and core threads. If the blocking queue is full, it checks to see if the current maximum number of threads has been reached. If not, it continues to start new threads to process tasks until the maximum number of threads has been reached.
Blocking queue: workQueue
A waiting area is a blocking queue. If the core thread is busy, the task enters the blocking queue first.
Keep available time: keepAliveTime
Windows 4 and 5 will be off if they haven’t been working for a while, because they’re already on vacation today, which means that if a non-core thread hasn’t been working for a while, it will automatically destroy, and keepAliveTime is the time that it’s set to expire.
Time unit: unit
KeepAliveTime Unit of keepAliveTime.
ThreadFactory: threadFactory
The factory for the user to create threads is usually used by default, or can be customized if the requirements are not met.
Reject policy: Handler
If all the window is being dealt with business, waiting area is also full, this time can only look at how to deal with the bank manager, is the task of the blocking queue is full, the thread has reached the maximum number of threads, this time will go refused to strategy, system has to provide us with good several refused to strategy, we can also custom.
-
The code field
Create a bank thread pool with 3 core threads, 5 maximum threads, and 3 block queue size. Then add 5 tasks to see:
public class ThreadDemo {
private static int corePoolSize = 3; // Number of core threads
private static int maximumPoolSize = 5; // Maximum number of threads
private static long keepAliveTime = 10; // The number of non-core threads that can survive without a task
private static TimeUnit unit = TimeUnit.SECONDS; // Unit of survival time
private static BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(3); // Task blocking queue
private static ThreadFactory threadFactory = Executors.defaultThreadFactory(); // Thread factory
private static RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // Reject the policy
private static ArrayList<String> mList = new ArrayList<>(); // Task list
public static void main(String[] args) {
// Create 5 tasks
for (int i = 0; i < 5; i++) {
mList.add("Task" + i);
}
ThreadPoolExecutor executor =
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
unit, workQueue, threadFactory, handler);
for (String s : mList) {
executor.execute(new Runnable() {
@Override
public void run(a) {
System.out.println(Thread.currentThread().getName() + "Executed ->"+ s); }}); } executor.shutdown(); }} Result: Pool -1-thread-1Performed the -> task0
pool-1-thread-3Performed the -> task2
pool-1-thread-1Performed the -> task3
pool-1-thread-3Performed the -> task4
pool-1-thread-2Performed the -> task1
Copy the code
The execution results just correspond to the figure above. Windows 1, 2 and 3 execute all 5 tasks.
Now let’s look at seven tasks:
// Create 7 tasks
for (int i = 0; i < 7; i++) {
mList.add("Task" + i);
}
pool-1-thread-1Performed the -> task0
pool-1-thread-3Performed the -> task2
pool-1-thread-2Performed the -> task1
pool-1-thread-3Performed the -> task4
pool-1-thread-2Performed the -> task5
pool-1-thread-1Performed the -> task3
pool-1-thread-4Performed the -> task6
Copy the code
As we can see, window 4 also starts to perform tasks
Let’s take a look at 20 tasks:
// Create 20 tasks
for (int i = 0; i < 20; i++) {
mList.add("Task" + i);
}
pool-1-thread-1Performed the -> task0
pool-1-thread-3Performed the -> task2
pool-1-thread-2Performed the -> task1
pool-1-thread-3Performed the -> task4
pool-1-thread-1Performed the -> task3
pool-1-thread-4Performed the -> task6
pool-1-thread-1Performed the -> task9
pool-1-thread-5Performed the -> task7
pool-1-thread-3Performed the -> task8
pool-1-thread-2Performed the -> task5
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task senior.thread.ThreadDemo$1@266474c2 rejected from java.util.concurrent.ThreadPoolExecutor@6f94fa3e[Running, pool size = 5, active threads = 4, queued tasks = 0, completed tasks = 6]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at senior.thread.ThreadDemo.main(ThreadDemo.java:37)
Copy the code
The task became 20 direct exceptions, because our code’s rejection policy was set to throw exceptions if the task couldn’t handle it
private static RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // Reject the policy
Copy the code
-
Rejection policies
ThreadPoolExecutor.AbortPolicy()
When a task is added to the thread pool is refused, it will throw RejectedExecutionException anomalies.
ThreadPoolExecutor.CallerRunsPolicy()
When a rejected task is added, the rejected task is executed on the thread that calls the execute method. The rejected task is not discarded until the Executor is disabled.
ThreadPoolExecutor.DiscardOldestPolicy()
When a task added to the thread pool is rejected, the thread pool discards the oldest unprocessed task in the wait queue, and then adds the rejected task to the wait queue.
ThreadPoolExecutor.DiscardPolicy()
When a task added to the thread pool is rejected, the thread pool discards the rejected task.
-
Closing the thread pool
shutdown()
Shutdown does not directly shutdown the thread pool. It only sets the state of the thread pool to SHUTWDOWN, and no new tasks are accepted. Ongoing tasks continue to be executed, and unexecuted tasks are interrupted.
shutdownNow()
ShutdownNow sets the state of the thread pool to STOP. Executing tasks are stopped and unexecuted tasks are returned.
-
Executors
Although Alibaba does not recommend us to use the factory provided by the government, we still need to learn
- Create a thread pool with cache: Executors. NewCachedThreadPool
public static ExecutorService newCachedThreadPool(a) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Copy the code
The core thread is 0, the maximum thread is the maximum Integer, and the timeout is 60 seconds. When a new task is executed, the available thread is reused if there are previously available threads in the thread pool, otherwise a new thread is created.
- Create a fixed number of thread pool: Executors. NewFixedThreadPool (5)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Copy the code
The core thread is equal to the maximum thread, which we send via nThreads to control the maximum number of concurrent threads, and the excess threads will wait in the queue.
- Create a single thread pool: Executors. NewSingleThreadExecutor ()
public static ExecutorService newSingleThreadExecutor(a) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
Copy the code
The core thread and the maximum number of threads are both 1. If the thread ends because of an exception, a new thread is created to continue the subsequent task.
- Create a scheduled thread pool :newScheduledThreadPool(5)
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
Copy the code
Create a thread pool that supports timed and periodic task execution.
-
conclusion
- Thread pooling reduces resource consumption and avoids frequent thread creation and destruction.
- Improve the response speed of task execution, task execution can be directly used by the thread pool has been created without waiting for the thread to be created.
- Improve thread management, can manage threads in a unified manner.