background
I believe that many small partners in work will use thread pool, but the definition and use of thread pool is a complex process, because there are as many as 7 thread pool constructors alone, and the definition of each parameter is not a simple basic data structure. Just pass it in directly
Therefore, some inexperienced partners use the default thread pool provided by jdk to build tool class Executors
Executors do provide some thread pools for easy use, but they are unbounded queues. Too many tasks are prone to OOM. Alibaba specification also prohibits the use of executors to construct thread pools
Although some of Alibaba's own open source middleware do not comply with this specification, it does not prevent it from being a good specification
Get task exception log
It should also be noted that errors occur during the execution of tasks by the thread pool. The default UncaughtExceptionHandler policy is to print to the console
This is just a small problem. The troublesome problem is that when submitting a task using the submit method of the thread pool, exception information will not be printed when an exception occurs. Errors will be printed only when obtaining the task results. Some people will submit a task using the submit method when they don't need to obtain the task results, resulting in an exception and don't know. This is very stupid, Like this
ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.submit(() -> { int i = 1 / 0; });
We don't know if there is an exception. Just look at the source code
The thread executing the task is encapsulated as a RunnableFuture object. Let's look at the run method of the RunnableFuture object
The exception thrown will be put into the outcome object, which is the result of the FutureTask object returned by the submit() method executing the get() method
Therefore, do not use the submit method if you do not need to obtain the task results in the thread
The number of threads in the thread pool does not know how to set
A design of dynamic thread pool is popular in the industry. I also wrote how to implement a dynamic thread pool before
https://weihubeats.blog.csdn.net/article/details/113751243
However, there is a calculation formula for the number of threads in the thread pool
- I/O intensive: optimal number of threads = number of CPU cores * [1 + (I/O time consumption / CPU time consumption)]
- CPU intensive: optimal number of threads CPU+1
The thread number setting of thread pool needs to be solved
Thread pool name
The default name of thread pool is pool-x-thread-x. it is also very inconvenient to locate the problem of thread pool on our line. We need to specify an appropriate prefix for each thread pool
Coding implementation
Based on the above problems, let's optimize our own thread pool construction tool class
First, to solve the problem of no printing error in thread pool and the definition of thread name prefix, we define a custom thread creation factory
ThreadFactoryImpl
public class ThreadFactoryImpl implements ThreadFactory { private final AtomicLong threadIndex = new AtomicLong(0); private final String threadNamePrefix; private final boolean daemon; public ThreadFactoryImpl(final String threadNamePrefix) { this(threadNamePrefix, false); } public ThreadFactoryImpl(final String threadNamePrefix, boolean daemon) { this.threadNamePrefix = threadNamePrefix; this.daemon = daemon; } @Override public Thread newThread(@NotNull Runnable r) { Thread thread = new Thread(r, threadNamePrefix + this.threadIndex.incrementAndGet()); thread.setDaemon(daemon); return thread; } }
Then define a thread pool tool class and related tool methods
ThreadPoolUtil
public static ThreadFactory createThreadFactory(String threadNamePrefix, boolean daemon) { if (threadNamePrefix != null) { return new ThreadFactoryImpl(threadNamePrefix, daemon); } return Executors.defaultThreadFactory(); }
Next is the core implementation of the custom thread pool construction tool class
ThreadPoolBuilder
public class ThreadPoolBuilder { private static final RejectedExecutionHandler defaultRejectHandler = new ThreadPoolExecutor.AbortPolicy(); /** * cpu Kernel number */ private static final int CPU = SystemUtil.getCPU(); /** * create io ThreadPoolExecutor * * @return ThreadPoolExecutor */ public static IOThreadPoolBuilder ioThreadPoolBuilder() { return new IOThreadPoolBuilder(); } /** * create cpu ThreadPoolExecutor * * @return */ public IOThreadPoolBuilder CPUPool() { return new IOThreadPoolBuilder(); } public static class IOThreadPoolBuilder { private ThreadFactory threadFactory; private RejectedExecutionHandler rejectHandler; private int queueSize = -1; private int maximumPoolSize = CPU; private int keepAliveTime = 120; private boolean daemon = false; private String threadNamePrefix; public int getCorePooSize(int ioTime, int cpuTime) { return CPU + (1 + (ioTime / cpuTime)); } public IOThreadPoolBuilder setThreadNamePrefix(String threadNamePrefix) { this.threadNamePrefix = threadNamePrefix; return this; } public IOThreadPoolBuilder setDaemon(boolean daemon) { this.daemon = daemon; return this; } public IOThreadPoolBuilder setRejectHandler(RejectedExecutionHandler rejectHandler) { this.rejectHandler = rejectHandler; return this; } public IOThreadPoolBuilder setQueueSize(int queueSize) { this.queueSize = queueSize; return this; } public IOThreadPoolBuilder setMaximumPoolSize(int maximumPoolSize) { this.maximumPoolSize = maximumPoolSize; return this; } public IOThreadPoolBuilder setKeepAliveTime(int keepAliveTime) { this.keepAliveTime = keepAliveTime; return this; } public ThreadPoolExecutor builder(int ioTime, int cpuTime) { BlockingQueue<Runnable> queue; if (rejectHandler == null) { rejectHandler = defaultRejectHandler; } threadFactory = ThreadPoolUtil.createThreadFactory(this.threadNamePrefix, this.daemon); queue = queueSize < 1 ? new LinkedBlockingQueue<>() : new ArrayBlockingQueue<>(queueSize); return new ThreadPoolExecutor(getCorePooSize(ioTime, cpuTime), maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, queue, threadFactory, rejectHandler); } } }
Here, you can impose a mandatory limit on the number of queues. If it is not specified, you can also report an error
use
ThreadPoolExecutor executor = ThreadPoolBuilder .ioThreadPoolBuilder() .setThreadNamePrefix("io-test") .setMaximumPoolSize(20) .builder(10, 20); executor.execute(() -> { System.out.println(Thread.currentThread().getName()); int i = 1 / 0; });
summary
We can see that the creation of thread pool is still relatively complex, but we can encapsulate some thread pools suitable for our own development. For example, thread pool of IO type, thread pool of CPU type, create CachedThreadPool, etc