Thread pool for Java multithreading

What is a thread pool

A thread pool is a resource pool that manages a series of threads. When there is a task to be processed, the thread is obtained directly from the thread pool to process, and after processing, the thread
It will not be destroyed immediately, but wait for the next task.

Daemon thread

Provide services for user threads (ordinary threads), and when all user threads end, the remaining daemon threads end. GC is a daemon thread.

Why use thread pool

  • Pooling technology must have been seen by everyone. Thread pools, database connection pools, Http connection pools, etc. are all applications of this idea. The idea of pooling technology is mainly to reduce the consumption of each resource acquisition and improve the utilization of resources.

  • The thread pool can limit and manage thread resources. Each thread pool also maintains some basic statistics, such as the number of completed tasks.

  • Benefits of using a thread pool:

    • Reduce resource consumption. Reduce the cost of thread creation and destruction by reusing created threads.
    • Improve responsiveness. When a task arrives, the task can be executed immediately without waiting for the thread to be created.
    • Improve thread manageability. Threads are scarce resources. If created without limit, it will not only consume system resources, but also reduce the stability of the system. Using thread pool can be used for unified allocation, tuning and monitoring.

Notes on using thread pool

  • Manually create a thread pool

  • Reasonable number of threads

  • Consider the impact of multiple thread pools

Parameters of the thread pool construction method

  • corePoolSize: The number of core threads. After the thread pool is initialized, by default, there are no threads in the thread pool. The thread pool will wait for a task to arrive before creating a thread to perform the task
  • maxPoolSize: the maximum number of threads, on the basis of the number of core threads, the upper limit of the number of additional threads is increased
  • keepAliveTime: When the number of threads is more than corePoolSize, if the idle time of the redundant threads exceeds keepLiveTime, the redundant threads will be terminated
  • ThreadFactory: used to create threads, Executors.defaultThreadFactory() is used by default, and the created threads are in the same thread group with a priority of 5.
    If you specify ThreadFactory yourself, you can change the thread name, thread group, priority, whether it is a daemon thread, etc.
  • workQueue: There are 3 common queue types:
    • Synchronous queue: SynchronousQueue has no internal storage. When using it, the maximum number of threads needs to be set larger. If there are not many tasks, you can use it for simple transfer
    • Unbounded queue: LinkedBlockingQueue If the number of threads reaches corePoolSize, the maxPoolSize parameter is useless because it will never be full, it can prevent traffic surges and put all tasks into the queue, but if the processing speed cannot keep up with the task submission speed , it will cause memory waste or OOM exception.
    • Bounded queue: ArrayBlockingQueue can be used in conjunction with maxPoolSize

Add thread rules

  1. When the total number of threads is less than corePoolSize at the beginning, even if some threads are idle, a new thread will be created when a new task arrives
  2. Until the number of threads is equal to corePoolSize, when the task comes again, the task will be put into the queue for execution.
  3. Until the queue is full, if the number of threads is less than maximumPoolSize at this time, a new thread will be created to perform the task.
  4. If the queue is full and the number of threads reaches maximumPoolSize, when trying to add tasks, it will be rejected.

Features of increasing or decreasing threads

  1. By setting corePoolSize and maximumPoolSize to be the same, a fixed-size thread pool can be created.
  2. The thread pool wants to keep the number of threads low and only increase the number of threads when the load becomes heavy.
  3. By setting maximumPoolSize to Integer.MAX_VALUE, it is possible to allow the thread pool to accommodate any number of concurrent tasks.
  4. More threads than corePoolSize are only created when the queue is full, and if you are using an unbounded queue, the number of threads will never exceed corePoolSize.

The thread pool is best created manually

Manually create running rules that can clarify the thread pool more clearly to avoid the risk of resource exhaustion

Four automatically created thread pools:

  • newFixedThreadPool: The maximum number of threads is the same as the number of core threads, using an unbounded queue. When requests pile up, it will take up a lot of memory, resulting in OOM

  • newSingleThreadExecutor: The maximum number of threads and the number of core threads are both 1, using an unbounded queue. OOM will also be generated.

  • newCachedThreadPool: A cacheable thread pool with the function of automatically reclaiming redundant threads. maximumPoolSize is set to Integer.MAX_VALUE , which may create a lot of threads, resulting in OOM

  • newScheduledThreadPool: A thread pool that supports timing and periodic task execution. Using the delay queue DelayedWordQueue, you can delay tasks according to time

    The correct way to create a thread pool: according to different business conditions, set the thread pool parameters, such as: how much memory, what name to give to the thread, etc.

How to set the number of threads

  • CPU-intensive (encryption, computing hash, etc.): the optimal number of threads is 1-2 times the number of CPU cores
  • Time-consuming IO type (reading and writing databases, files, network reading and writing, etc.): the optimal number of threads is generally many times greater than the number of CPU cores

Characteristics of common thread pool

Construction method parameters of the four thread pools

FixedThreadPool

  • The number of core threads and the maximum number of threads are the same
  • After the queue is full, the thread cannot be added

CachedThreadPool

  • cacheable thread pool

  • Has the function of automatically reclaiming redundant threads

  • Queue has no capacity

  • The number of threads can be many

ScheduledThreadPool

  • Thread pool that supports timing and periodic task execution

SingleThreadExecutor

  • Single-threaded thread pool: only the only worker thread is used to perform tasks
  • The principle is the same as FixedThreadPool, except that the number of threads is set to 1

Close the thread pool

  • The shutdown() method will not directly close the thread pool, but will not add new tasks

  • isShutdown() method to determine whether the thread pool is closed

  • The isTerminated() method can determine whether the thread pool is completely closed

  • awaitTermination(3L, TimeUnit.SECONDS) Within 3s after executing this method, the thread is blocked and judges whether the thread pool is completely closed

  • The shutdownNow() method directly closes the thread pool and returns to the waiting queue

Pause and resume thread pool

4 rejection strategies

  1. AbortPolicy directly throws an exception
  2. DiscardPolicy silently discards tasks without telling you
  3. DiscardOldestPolicy silently discards the oldest task without notification
  4. CallerRunsPolicy If the thread pool is full, let the thread that submitted the task execute

ReentrantLock and Condition

ReentrantLock is a common “lock” in Java, which can “lock” some resources, so that these resources can only be operated after acquiring the lock, which ensures thread safety, otherwise, if there are many If several threads operate on the resource at the same time, the data may be wrong.

  • The lock() / unlock() method is to lock / unlock. It is often used to operate the same block of memory in multiple threads, but only one thread is allowed to operate at the same time.

    Condition is an internal tool of ReentrantLock, which can realize the effects of “waiting” and “wake up”. Waiting”, and the thread executing task B will go to the thread in the “waiting” state before “wake up” after completing the task.

  • await() method: the current thread enters the waiting state, if other threads call the signal or signalAll method of the condition and the current thread obtains the Lock and returns from the await method

  • signal() method: wake up a thread waiting on the condition, transfer the thread from the waiting queue to the synchronization queue, and return from the waiting method if the Lock can be competed in the synchronization queue.

The hook method realizes the suspension and recovery of threads

public class PauseableTreadPool extends ThreadPoolExecutor {<!-- -->

    private boolean is Paused;
    private final ReentrantLock lock = new ReentrantLock();
    private Condition unPaused = lock. newCondition();

    @Override
    protected void beforeExecute(Thread t, Runnable r) {<!-- -->
        super. beforeExecute(t, r);
        lock.lock();//lock
        try {<!-- -->
            while (isPaused) {<!-- -->
                unPaused.await();//Let the thread wait
            }
        } catch (InterruptedException e) {<!-- -->
            e.printStackTrace();
        }finally {<!-- -->
            lock.unlock();//release the lock
        }

    }
    //pause
    private void pause() {<!-- -->
        lock. lock();
        try {<!-- -->
            isPaused = true;
        } finally {<!-- -->
            lock. unlock();
        }
    }
    //recover
    private void resume() {<!-- -->
        lock. lock();
        try {<!-- -->
            isPaused = false;
            unPaused.signalAll();//Wake up the thread
        }finally {<!-- -->
            lock. unlock();
        }
    }
}

Parts of the thread pool

  • Thread pool manager (create, stop thread pool, etc.)
  • Worker threads (threads used to execute tasks, repeatedly fetched from the queue)
  • Queue (there are many kinds, must support concurrency)
  • Task interface (Task)

The principle of thread pool to realize task multiplexing

The same thread performs different tasks

Thread pool status