• 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

  1. Thread pooling reduces resource consumption and avoids frequent thread creation and destruction.
  2. 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.
  3. Improve thread management, can manage threads in a unified manner.