JAVA multithreading thread pool, also Mengquan come and have a look!!

Keywords: Java thread pool

On thread pool in high concurrency and multithreading

definition

Thread is a scarce resource. Its creation and destruction is a relatively heavy and resource consuming operation. Java thread depends on kernel thread. Creating thread requires operating system state switching. In order to avoid excessive resource consumption, it is necessary to try to reuse thread to perform multiple tasks. Thread pool is a thread cache, which is responsible for unified allocation, tuning and monitoring of threads.

When to use thread pools:

  • The processing time of a single task is relatively short
  • The number of tasks to be processed is relatively large

Advantages of thread pool:

  • Reuse existing threads, reduce the overhead of thread creation and extinction, and improve performance
  • Improve response speed. When the task arrives, the task can be executed immediately without waiting for the thread to be created.
  • Improve thread manageability, unified allocation, tuning and monitoring.

How thread pools are created

Executors under JavaJUC package provide four ways to create thread pools and underlying parameters (the underlying of the four ways to create threads uses the ThreadPoolExecutor class, which is defined according to different parameters, and the description of relevant parameters will be given below)

newCachedThreadPool

Definition and function

Create a thread pool that can create new threads as needed. If there are available threads in the thread pool (available means that the threads exist and are idle), if not, create a new thread to execute. It is usually used to execute asynchronous tasks with short time

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//The number of core threads corePoolSize is 0, and maximumPoolSize is close to infinity
                                      60L, TimeUnit.SECONDS,//The survival time of keepAliveTime is 60s, and it will be recycled automatically when the time is up
                                      new SynchronousQueue<Runnable>());//Synchronous blocking queue is used
    }

Usage example

//Define a thread class
class RunnableThread implements Runnable{
    private int i=0;
    public RunnableThread(int i) {
        this.i = i;
    }
     public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"The second paragraph was implemented"+i+"A task!");
        }
    }
//Create 10 tasks
   ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new RunnableThread(i));

        }

You can see that 10 tasks were performed in one second

newScheduledThreadPool

Definition and function

Create a thread pool that supports periodic execution and can be used as a scheduled task. It is mainly used in the scenario of periodic task execution

  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,//The maximumPoolSize is close to infinity, the keepAliveTime lifetime is 0 nanoseconds, and the thread will not be recycled
              new DelayedWorkQueue());//The delay queue is adopted, and the submitted tasks are sorted into the queue according to the execution time
    }

Usage example

  //schedule is a method unique to ScheduledExecutorService
ScheduledExecutorService  executorService = Executors.newScheduledThreadPool(6);
            executorService.schedule(new RunnableThread(1),6L,TimeUnit.SECONDS);
            executorService.schedule(new RunnableThread(2),5L,TimeUnit.SECONDS);
            executorService.schedule(new RunnableThread(3),4L,TimeUnit.SECONDS);
            executorService.schedule(new RunnableThread(4),3L,TimeUnit.SECONDS);
            executorService.schedule(new RunnableThread(5),2L,TimeUnit.SECONDS);


You can see that tasks are executed according to the preset delay. If timed tasks need to be done, you can use this thread to implement them

newFixedThreadPool

Definition and function

Create a thread pool with a fixed number of threads as needed. When there are many tasks, queue up and wait. It is suitable for the scenario with a fixed number of tasks and stability, that is, to create the specified number of threads when the concurrency pressure is determined

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,//The number of core threads is equal to the maximum number of threads
                                      0L, TimeUnit.MILLISECONDS,//nanosecond
                                      new LinkedBlockingQueue<Runnable>()); //The queue is blocked. When there are no available threads when the task volume comes, the queue will wait
    }

Usage example

   ExecutorService  executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 6; i++) {
            executorService.execute(new RunnableThread(i));
        }

You can see that three threads execute once in the first second, and the three threads execute repeatedly in the second second second, without creating a new thread

newSingleThreadExector

Definition and function

Create a thread pool with only a single thread and use the only working thread to execute tasks to ensure the orderly execution of tasks. It is applicable to the case where tasks are required to be carried out in order. It is the same as the definition of newFixedThreadPool, but there is only one thread

 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

Usage example

   ExecutorService  executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new RunnableThread(i));
        }

You can see that the tasks are executed one by one in a second and are carried out in an orderly manner

The above is the creation of four thread pools provided by Executors. It is clear from the above that the bottom layer is determined by using ThreadPoolExecutor according to different parameters. This class is mainly used to analyze the underlying principle of thread pool.

ThreadPoolExecutor

//Construction method
public ThreadPoolExecutor(int corePoolSize,  //Number of core threads
                              int maximumPoolSize,  //Maximum number of threads
                              long keepAliveTime,  //survival time 
                              TimeUnit unit,  //Time unit
                              BlockingQueue<Runnable> workQueue,  //Using the queue type, tasks can be stored in the task queue and wait to be executed. The FIFIO principle (first in, first out) is executed
                              ThreadFactory threadFactory,  //It is a thread worker that creates threads. You can create a thread in your own custom way
                              RejectedExecutionHandler handler)//It is a rejection policy. After a task is full, we can refuse to execute some tasks according to the adopted policy. java provides four execution strategies:
   			    // AbortPolicy: interrupt throws an exception
    			//Discard policy: silently discard the task without any notification
    			//DiscardOldestPolicy: discards the task that has been in the queue for the longest time
    			//CallerRunsPolicy: let the thread submitting the task execute the task (compared with the first three, it's more friendly)

Thread execution principle (it will be more clear in combination with the source code)


In actual project development, it is also recommended to use the method of manually creating thread pool instead of the default method. This is described in Alibaba development specification:


Create a custom thread pool, and create a thread pool according to the concurrency and specific task requirements

 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 0L, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(10));
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(new RunnableThread(i)
            );
        }

execute source code: the description of the source code will be supplemented later

   int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { //If the number is less than the number of core threads, execute down
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { //
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command); //Adopt rejection strategy

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

That's all for the basic use and principle of thread pool. You are welcome to point out your shortcomings and learn together!!

Posted by samtwilliams on Sun, 24 Oct 2021 09:18:10 -0700