Today’s sharing started, please give us more advice ~

Thread pool (Executor)

What is a thread pool?

Java5 introduces a new concurrency API called the Executor framework to make programmers’ lives easier. It simplifies the design and development of multithreaded applications. It mainly consists of Executor, the ExecutorService interface, and the ThreadPoolExecutor class, which implements both the Executor and ExecutorService interfaces. The ThreadPoolExecutor class provides an implementation of thread pools. We will learn more about this later in the tutorial.

Why do we need thread pools?

When we create a simple multithreaded application, we create Runnable objects and use Runnable to construct thread objects that we need to create, execute, and manage. We may have a hard time doing that. The Executor framework does this for you. It is responsible for creating, executing, and managing threads, and not only that, it improves application performance.

When you create a new thread for each task, then if the system is highly overloaded, you will get out of memory errors, the system will fail, and even throw oom exceptions. If ThreadPoolExecutor is used, no threads are created for new tasks. Assign tasks to a limited number of threads to execute only Runnable tasks. Once a thread completes a task, it blocks the queue for a Runnable to execute.

How do I create a thread pool?

There is another interface called ExecutorService, which extends the Executor interface. It can be called an Executor, and it provides methods that control termination and generate methods that track the progress of one or more asynchronous tasks in the future. It has methods like submit, shut down, shut down now, and so on.

ThreadPoolExecutor is the actual implementation of ThreadPool. It extends the implement AbstractThreadPoolExecutor ExecutorService interface. ThreadPoolExecutor can be created from the factory method of the Executor class. It is recommended that you use a method to obtain an instance of ThreadPoolExecutor.

  • Use the Executors Factory method to create a thread pool:

Provide default static methods

There are four factory methods available for obtaining instances of ThreadPoolExecutor in the Executors class. We’re using Executors’ newFixedThreadPool to get an instance of ThreadPoolExecutor.

ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);

  • Create thread pools for custom ThreadPoolExecutor

Provide a default constructor

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,

TimeUnit unit,BlockingQueue workQueue ,ThreadFactory threadFactory,RejectedExecutionHandler handler) ;

ThreadPoolExecutor source code analysis

  • Internal thread pool state

The CTL variable uses 29 bits lower to indicate the number of threads in the thread pool and 3 bits higher to indicate the running state of the thread pool:

  • RUNNING: -1 << COUNT_BITS, the thread pool in this state receives new tasks and processes tasks in the blocking queue;

  • COUNT_BITS: 0 << COUNT_BITS, that is, the high three bits are 000. The thread pool in this state will not receive new tasks, but will process tasks in the blocking queue.

  • STOP: 1 << COUNT_BITS, i.e. 001, the thread in this state does not receive new tasks, does not process tasks in the blocking queue, and interrupts running tasks.

  • TIDYING: 2 << COUNT_BITS, i.e. 010, all tasks have terminated;

  • TERMINATED: 3 << COUNT_BITS, that is, the high three bits are 011. TERMINATED: 3 << COUNT_BITS.

State transition diagram

Let’s take a look at some of the core methods inside ThreadPoolExecutor:

– Add a task: execute(Runnable command)

Execute the Runnable entry method

– Add work queue addWorker(Runnable firstTask, Boolean core)

Let’s see how to add worker threads

  • Run a task: runWorker(Worker w)

When addWorker succeeds, the Worker’s start() method is called. Next, let’s analyze how the task is executed.

We haven’t yet seen how worker threads are reclaimed when the number of threads >coreSize, so don’t worry, let’s look at the getTask() method.

– getTask task: getTask()

– shutdown the thread: shutdown()

– Shut down the thread now: shutdownNow()

This method interrupts the task execution and returns an unexecuted task

Considerations for thread pool usage

– use ThreadLocal

ThreadLocal is called thread-local storage and is typically used as a static field, providing a copy of its value for each thread that uses it. Thread-local storage is commonly used for database connections and transactions. ThreadLocal can be thought of simply as a container that stores a value object in a Map<Thread, T> field, that is, a Map using the current Thread as the key, ThreadLocal’s get() method retrieves the value object from the Map associated with the current thread. The actual implementation of ThreadLocal isn’t like this, but it can be understood simply like this. Threads in the thread pool are reused after the task completes, so when the thread completes, the ThreadLocal is cleaned up (removing the value object associated with the thread). Otherwise, a thread that is being reused to perform a new task will use the value object that was manipulated by the previous thread, resulting in undesirable results.

– Set a reasonable number of threads

Novice may have an error to use a thread pool, the higher the concurrent use more threads, however the actual situation is too much thread will cause a lot of system Context – the Switch which influence the system throughput, so reasonable number of threads need to combined with the project to make the pressure test, usually we mainly aimed at 2 types of task set rules of number of threads to:

1. The CPU intensive

CoreSize == Number of CPU cores +1

2. Io intensive

CoreSize == 2* Number of CPU cores

Today’s share has ended, please forgive and give advice!