background

I believe that many people in work will use thread pool, but the definition of thread pool is a relatively complicated process, because there are as many as 7 thread pool constructors alone, and the definition of each parameter is not simple basic data structure directly passed in

So for those who don’t have the experience, use the default thread pool provided by the JDK to build the tool Executors

Executors do provide some thread pool constructs for easy use, but all of them are unbounded queues, and too many tasks may cause OOM. Alibaba specification also prohibits the use of Executors to construct thread pools

Although some of Ali’s own open source middleware does not comply with the specification, that does not prevent it from being a good specification

Obtain the task exception log

Note that the default UncaughtExceptionHandler policy for thread pool errors during task execution is printed to the console

This is just a small problem, the problem is more troublesome method is to use a thread pool submit submit tasks in an exception occurs will not print exception information, only when the results of retrieval task will print error, when some people don’t need to get the task results will use the submit to submit this way, the task will lead to abnormal also don’t know, This is a bad one, like this

ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(() -> {
            int i = 1 / 0;
        });
Copy the code

We don’t know what happened. 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 is placed into the Outcome object, which is the result of FutureTask executing the GET () method that is returned by submit()

Do not use the Submit method if you do not need to retrieve the result of the task

The number of threads in the thread pool is not set

A dynamic thread pool is a popular design in the industry. I have written before about how to implement a dynamic thread pool

Weihubeats.blog.csdn.net/article/det…

However, there is generally a formula for calculating the number of threads in a thread pool

  • I/O intensive: Optimal number of threads = Number of CPU cores * [1 + (I/O time/CPU time)]
  • CPU intensive: The optimal number of threads is CPU+1

The thread count setting for the thread pool needs to be resolved

Thread pool name

The default thread pool name is pool-x-thread-x. It is also very inconvenient to locate thread pool problems on our line. We need to specify the appropriate prefix for each thread pool

coded

Based on these issues, let’s optimize a thread pool builder class of our own

To solve the problem of thread pool not printing errors and thread name prefixes, 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);
        returnthread; }}Copy the code

Then define a thread pool utility class with associated utility methods

ThreadPoolUtil

public static ThreadFactory createThreadFactory(String threadNamePrefix, boolean daemon) {
        if(threadNamePrefix ! =null) {
            return new ThreadFactoryImpl(threadNamePrefix, daemon);
        }

        return Executors.defaultThreadFactory();

    }
Copy the code

Next comes the core implementation of the custom thread pool builder utility class

ThreadPoolBuilder

public class ThreadPoolBuilder {

    private static final RejectedExecutionHandler defaultRejectHandler = new ThreadPoolExecutor.AbortPolicy();

    /** * Number of CPU cores */
    private static final int CPU = SystemUtil.getCPU();


    /**
     * create io ThreadPoolExecutor
     *
     * @return ThreadPoolExecutor
     */
    public static IOThreadPoolBuilder ioThreadPoolBuilder(a) {
        return new IOThreadPoolBuilder();
    }

    /**
     * create cpu ThreadPoolExecutor
     *
     * @return* /
    public IOThreadPoolBuilder CPUPool(a) {
        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 newThreadPoolExecutor(getCorePooSize(ioTime, cpuTime), maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, queue, threadFactory, rejectHandler); }}}Copy the code

You can impose a limit on the number of queues, or you can report an error if you do not specify it

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;
        });
Copy the code

conclusion

As you can see, thread pool creation is still relatively complex, but we can encapsulate some thread pools that are suitable for our own development. Such as IO thread pool, CPU thread pool, CachedThreadPool, etc

Feel good article welcome to pay attention to the public number: small play technology