Summary:

Thread Pool is a tool to manage threads based on pooling idea, which often appears in multi-threaded server, such as MySQL. Too many lines will bring extra costs, including the cost of creating and destroying threads, the cost of scheduling threads, etc., and also reduce the overall performance of the computer. A thread pool maintains multiple threads waiting for a supervisor to assign tasks that can be executed concurrently. This approach, on the one hand, avoids the cost of creating and destroying threads while processing tasks, on the other hand, avoids the excessive scheduling problem caused by the expansion of the number of threads, and ensures the full utilization of the kernel.

Advantages of thread pools:

1. Reduce system resource consumption by reusing existing threads to reduce consumption caused by thread creation and destruction;

2. Improve the system response speed. When a task arrives, it can be executed immediately without waiting for the creation of a new thread by reusing the existing thread;

3. Facilitate the control of concurrent threads. This is because threads created without limit can take up too much memory to generate OOM, and can cause excessive CPU switching (CPU switching has a time cost (the need to keep the current thread of execution alive and restore the thread of execution).

4. Provide more powerful function, delay timing thread pool.

Net the most detailed thread pool N kinds of use, attached source code example, take away the use of Java thread pool there are several as follows:

There are 4 built-in thread pools in JDK (after JDK1.5)

1. Fixed thread pool (newFixedThreadPool)

The threads in this thread pool are designed to hold a fixed number of threads, which can be considered as the number of CPU cores x N(N can be large or small, depending on the number of concurrent threads, the hardware resources available to the computer, etc.). To get the number of CPU cores on the current computer, use the following code.

int processors = Runtime.getRuntime().availableProcessors();

FixedThreadPool is through Java. Util. Concurrent. Executors ThreadPoolExecutor created instance. This example will reuse a fixed number of threads to process a shared borderless queue. A maximum of nThreads will be active at any one time. If too many tasks are submitted while all threads are active, it will consistently wait in the queue until a thread is available. If any thread aborts due to an error during execution, a new thread takes its place to perform subsequent tasks. All threads are consistently stored in the thread pool until an explicit executorservice.shutdown () is shutdown. Because the blocking queue uses LinkedBlockingQueue, which is an unbounded queue, it is never possible to reject a task. LinkedBlockingQueue uses a different Lock when enqueued and when enqueued, meaning that there is no mutual exclusion between them, and in multi-CPU cases they can consume and produce at the same time in true parallel. Therefore, this thread pool does not reject tasks, nor does it initiate new threads, nor does it destroy threads because they have not been used for a long time. This is a typical producer —- consumer problem, and this thread pool is suitable for stable and fixed concurrency scenarios, such as servers. The following code shows a fixed number of threads for the DEMO, with each core bound to 5 threads.

Case study:

import java.util.Random;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Test {

public static void main(String[] args) {

// Get the computer has several cores

int processors = Runtime.getRuntime().availableProcessors();

// The first type of thread pool is a fixed number of thread pools that can be bound to a certain number of threads per CPU core

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(processors * 5)

for (int i = 0; i < 10; i++) {

fixedThreadPool.execute(new Runnable() {

@Override

public void run() {

System.out.println(Thread.currentThread().getName());

}

});

}

fixedThreadPool.shutdown();

}

}

2. Cache thread pool (newCachedThreadPool)

The core pool size is 0 and the maximum number of threads in the thread pool is a maximum integer, which means that all tasks are added to the blocking queue as soon as they are submitted. The block queue is SynchronousQueue when a thread 60 seconds in the thread pool terminates without executing a task. SynchronousQueue takes a wait that requires a PUT operation to wait, and a put operation that requires a take operation to wait, or else blocks. (The blocking queue of the thread pool cannot be stored, so new threads are created to process requests while the current thread is busy.) To sum up: ① This is a thread pool that can be expanded indefinitely; (2) Suitable for processing tasks with relatively small execution time; (3) Threads will be killed if they are idle for more than 60 seconds, so when they are idle for a long time, this thread pool occupies almost no resources. (4) There is no storage space in the blocking queue. As long as the request arrives, an idle thread must be found to process the request. If the request cannot be found, a new thread must be created in the thread pool. If the main thread submits tasks faster than CachedThreadPool can process them, CachedThreadPool will constantly create new threads to execute the tasks, which may exhaust CPU and memory resources. Therefore, it is important to control the number of concurrent tasks when using this thread pool. Otherwise creating a large number of threads can cause serious performance problems.

Case study:

import java.util.Random;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Test {

public static void main(String[] args) {

// Cache thread pool, unlimited

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

for (int i = 0; i < 100; i++) {

cachedThreadPool.execute(new Runnable() {

@Override

public void run() {

System.out.println(Thread.currentThread().getName());

}

});

}

cachedThreadPool.shutdown();

}

}

3. Thread pool for a single thread (newSingleThreadExecutor)

SingleThreadExecutor is an Executor that uses a single worker thread. As a thread pool for a single worker thread, SingleThreadExecutor sets corePool and maximumPoolSize to 1. The unbounded queue LinkedBlockingQueue is used as a FixedThreadPool, so it has the same impact as a FixedThreadPool. In the case of newSingleThreadExecutor(), when a thread throws an exception while it is running, a new thread joins the thread pool to do the next task for it. Create a single threaded thread pool that uses only one worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority), so this is suitable for scenarios where tasks need to be executed in order. For example, less important finishing, logging, etc., can be done in a single threaded thread. Logging in general will be slow (large amount of data in general may not be written to the database), order will slow down the whole interface, accumulate more requests, may also affect the database (transaction) in the open, so logging can throw into single thread thread, processing lines, can also be regarded as a single producer consumer model of consumers.

Case study:

import java.util.Random;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Test {

public static void main(String[] args) {

// Single thread pool, always maintain one thread

ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

for (int i = 0; i < 10; i++) {

final int j = i;

singleThreadPool.execute(new Runnable() {

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + “:” + j);

}

});

}

singleThreadPool.shutdown();

}

}

4. Fixed number of thread pools (newScheduledThreadPool)

Compared with the first fixed number of thread pool, ① can execute delayed tasks, ② can also execute tasks with return values

Case study:

public class Test {

public static void main(String[] args) throws InterruptedException, ExecutionException{

// The fourth type of thread pool: fixed number of thread pool, compared to the first fixed number of thread pool, can execute delayed tasks ①, ② can also execute

// A task with a return value.

// scheduledThreadPool.submit(); Performs a task with a return value

/ / scheduledThreadPool. The schedule delay () is used to fulfill.

// A fixed number of thread pools, which can execute delayed tasks or tasks with return values.

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

FutureTask ft = new FutureTask<>(new Callable() {

@Override

public String call() throws Exception {

System.out.println(“hello”);

return Thread.currentThread().getName();

}

});

scheduledThreadPool.submit(ft);

// Get the return value from the FutureTask object.

String result = ft.get();

System.out.println(“result : ” + result);

// Execute a delayed task

scheduledThreadPool.schedule(new Runnable() {

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + ” : bobm!” );

}

}, 3L, TimeUnit.SECONDS);

}

}

Spring thread pool

Multi-threaded concurrency is often cumbersome, but if you use the Spring container to manage business beans, things are much easier. Spring encapsulates Java’s multithreading implementation, so you just need to focus on the flow of concurrent transactions and some of the concurrency load features, specifically how to use Spring to handle concurrent transactions.

Thread pools can be enabled in two ways: configuration files or annotations

Configuration file: Added the spring configuration file spring-threadpool.xml

The < beans XMLNS = “www.springframework.org/schema/bean…”

XMLNS: xsi = “www.w3.org/2001/XMLSch…” xmlns:aop=”www.springframework.org/schema/aop”

xmlns:tx=”www.springframework.org/schema/tx” xmlns:jdbc=”www.springframework.org/schema/jdbc”

XMLNS: context = “www.springframework.org/schema/cont…”

xmlns:mvc=”www.springframework.org/schema/mvc” xmlns:task=”www.springframework.org/schema/task”

Xsi: schemaLocation = “HTTP: / / www.springframework.org/schema/cont… default-autowire=”byName”>

Spring thread pool configuration for traffic messages

<task:annotation-driven executor=”messageExecutor”/>

<task:executor id=”asyncExecutor” pool-size=”100-10000″ queue-capacity=”10″/>

<task:executor id=”messageExecutor” pool-size=”15-50″ queue-capacity=”100″ keep-alive=”60″

rejection-policy=”CALLER_RUNS”/>

Note: Spring thread pool is enabled with the @enableAsync annotation. @async marks a method as an asynchronous method. When spring scans the method, a new thread will execute it

package cn.leadeon.message.test;

import org.springframework.scheduling.annotation.Async;

import org.springframework.scheduling.annotation.EnableAsync;

import org.springframework.stereotype.Component;

/ * *

  • @author LiJunJun

  • @since 2018/10/11

* /

@Component

@EnableAsync

public class AsyncTest {

@Async

public void test1() {

System.out.println(” Execute test1 asynchronously!! );

System.out.println(” Thread id: “+ thread.currentThread ().getid ());

System.out.println(” thread.currentThread ().getName() “);

}

@Async

public void test2() {

System.out.println(” Execute test2 asynchronously!!” );

System.out.println(” Thread id: “+ thread.currentThread ().getid ());

System.out.println(” thread.currentThread ().getName() “);

}

@Async

public void test3() {

System.out.println(” Execute test3 asynchronously!!” );

System.out.println(” Thread id: “+ thread.currentThread ().getid ());

System.out.println(” thread.currentThread ().getName() “);

}

}

You can import configuration files using annotations or import them in your own Spring configuration file.

package cn.leadeon.message.test;

import org.springframework.context.annotation.ImportResource;

import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; / * *

Note that the @enableAsync annotation is equivalent to the @enableAsync annotation and the @enableAsync annotation is equivalent to the @enableAsync annotation.

Customize your own thread pool

First, customize the thread base class, customize the exception handler, and customize the thread name. (Only the custom exception handler can use the Excute method to submit the thread. If the thread is submitted using the Submit method, the exception will be wrapped in the Future object and returned to the caller.)

class MyAppThread extends Thread{

private static final String DEFAULT_NAME=”TomDog”;

private static volatile boolean debugLifeCycle = true;

private static final AtomicInteger created=new AtomicInteger();

private static final AtomicInteger alive=new AtomicInteger();

private static final Logger log=Logger.getAnonymousLogger();

public MyAppThread(Runnable runnable){

this(runnable,DEFAULT_NAME);

}

public MyAppThread(Runnable runnable,String name){

super(runnable,name+”-“+created.incrementAndGet());

setUncaughtExceptionHandler(

new Thread.UncaughtExceptionHandler(){

public void uncaughtException(Thread t, Throwable e) {

System.out.println(“UNCAUGHT in thread “+t.getName());

}

}

);

}

@Override

public void run(){

boolean debug=debugLifeCycle;

if (debug){

System.out.println(“Created “+ getName());

}

try {

alive.decrementAndGet();

super.run();

}finally {

alive.decrementAndGet();

if (debug){

System.out.println(“Exiting “+getName());

}

}

}

public static int getCreated() {

return created.get();

}

public static int getAlive() {

return alive.get();

}

public static void setDebug(boolean debugLifeCycle) {

MyAppThread.debugLifeCycle = debugLifeCycle;

}

}

With the thread base class, you can create your own thread workshop, which is literally a workshop for producing threads.

class MyThreadFactory implements ThreadFactory{

private final String threadName;

MyThreadFactory(String threadName){

this.threadName=threadName;

}

public Thread newThread(Runnable r) {

return new MyAppThread(r,threadName);

}

}

Then create a custom thread pool, to implement the task execution time, can customize the rewrite ThreadPoolExecutor beforeExecute, afterExecute, terminated method to realize the record every task execution time, average execution time of tasks.

class MyTimingThreadPool extends ThreadPoolExecutor{

private final ThreadLocal startTime

=new ThreadLocal();

private final AtomicLong numTasks=new AtomicLong();

private final AtomicLong totalTime=new AtomicLong();

public MyTimingThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,

BlockingQueue workQueue, ThreadFactory threadFactory,

RejectedExecutionHandler handler) {

super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

}

@Override

protected void beforeExecute(Thread thread,Runnable runnable){

super.beforeExecute(thread,runnable);

System.out.println(String.format(“Thread %s:start %s”,thread,runnable));

startTime.set(System.nanoTime());

}

@Override

protected void afterExecute(Runnable runnable,Throwable throwable){

try {

long endTime=System.nanoTime();

long taskTime=endTime-startTime.get();

numTasks.incrementAndGet();

totalTime.addAndGet(taskTime);

System.out.println(String.format(” Thread %s :end %s, time=%dns”,throwable,runnable,taskTime));

}finally {

super.afterExecute(runnable,throwable);

}

}

@Override

protected void terminated(){

try {

System.out.println(String.format(“Terminated : avg time=%dns”,totalTime.get()/numTasks.get()));

}finally {

super.terminated();

}

}

}

According to ThreadPoolExecutor’s construction parameters, we also need to specify a task queue, a saturation policy, the number of core threads, the maximum number of threads, and the length of time that threads are active, depending on our business scenario.

For CPU-intensive tasks, you can specify the number of threads as number of CPU cores +1. This can be obtained in Java by runtime.getruntime ().availableProcessors().

Next, create a custom thread pool:

ThreadPoolExecutor executor=new MyTimingThreadPool(CPU_COUNT,maxPoolSize,keepAliveTime,timeUnit,blockingQueue,

threadFactory ,rejectedExecutionHandler);

Your custom thread pool is now complete.

In fact, in our system, tomcat has several thread pools, Dubbo has several thread pools, Kafka Client has several thread pools, and ZooKeeper Client has several thread pools……. How data is passed between threads to complete a request requires an understanding of clearing that is of interest to us in order to better understand the application scenario of thread pools.

Bottom line: Thread pooling is definitely going to be used in development, and if the Java language has coroutines like Golang, maybe Java programmers will have one less burden. After reading this article, have you learned? —