This is the 24th day of my participation in the August More Text Challenge

Multithreading has been the difficulty in Java development, but also in the interview of frequent, while there is still time, I intend to consolidate the JUC knowledge, I think the opportunity is everywhere, but is always left to prepare for the people, I hope we can come on!!

Go down, come up again, I think we’ll be different.

🍟 Thread pool introduction

1) What is thread pool?

Thread pool (English: Thread pool) : a thread usage pattern. A thread container maintained by the system and shared by all APPdomains controlled by the CLR. Thread pools can be used to perform tasks, send work items, handle asynchronous I/O, wait on behalf of other threads, and handle timers.

2) Why use thread pools?

Pain points:

  1. Without thread pools, each request creates a new thread and then destroys it, resulting in high resource consumption and low reuse rate.
  2. In Java, the thread stack occupied by a thread is stored outside the Java heap. It is not controlled by Java programs, but only limited by system resources (if the system gives insufficient resources, it may fail to create). By default, the stack size of a thread is 1M. If you create new threads for each request, 1024 threads will consume 1 GB of memory and the system will crash easily.
  3. If the system is large, it still creates new threads in the code every time, which is hard to manage, hard to find errors, and hard to monitor and tune.
  4. Too many lines will bring scheduling overhead, which will affect cache locality and overall performance.

The reason:

A thread pool maintains multiple threads, waiting for the supervisor to assign tasks that can be executed concurrently. This avoids the cost of creating and destroying threads while working on short-duration tasks. Thread pools not only ensure full utilization of the kernel, but also prevent overscheduling.

Advantages of thread pools:

The job of the thread pool is to control the number of threads running, queue tasks during processing, and then start those tasks after the thread is created. If the number of threads exceeds the maximum number, the exceeding number of threads queue up and wait for other threads to complete, and then pull the task from the queue to execute.

In addition, it is also mentioned in the Alibaba Java development manual that we often read:

[Mandatory] Thread resources must be provided through a thread pool. Explicit creation of threads in an application is not allowed.

Note: The benefit of using a thread pool is to reduce the time spent creating and destroying threads and the overhead of system resources, solving the problem of insufficient resources. If you don’t use thread pools, you can run out of memory or “overswitch” by creating a large number of similar threads.

3) characteristics

Reduced resource consumption: Reduces the cost of thread creation and destruction by reusing created threads.

Improved response time: When a task arrives, it can be executed immediately without waiting for a thread to be created.

Improve manageability of threads: Threads are scarce resources. If created without limit, they will not only consume system resources, but also reduce system stability. Thread pools can be used for uniform allocation, tuning, and monitoring.

4) architecture

Thread pools in Java are implemented through the Executor framework, which uses the Executor, ExecutorService, and ThreadPoolExecutor classes

Plus Executors handy tool class. However, using 😂 is not recommended

5) Thread pool parameter description

ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
Copy the code
  1. corePoolSizeThe number of core threads in the thread pool
    • When a task is submitted, if the number of threads in the current core thread pool does not reach corePoolSize, a new thread is created to execute the submitted task, even if there are idle threads in the current core thread pool. If the number of threads in the current core thread pool has reached corePoolSize, the thread will not be recreated.
  2. MaximumPoolSize Maximum number of threads that can hold
    • When the blocking queue is full and the number of threads in the current thread pool does not exceed maximumPoolSize, a new thread is created to execute the task.
  3. KeepAliveTime indicates the keepAliveTime of idle threads
    • If the number of threads in the current thread pool exceeds corePoolSize and the idle time exceeds keepAliveTime, these idle threads are destroyed to minimize system resource consumption.
  4. Unit Unit of survival time
  5. WorkQueue Stores queues of submitted but unexecuted tasks, that is, blocked queues.
  6. ThreadFactory Creates the factory class for the thread
  7. Handler Indicates the rejection policy for waiting for the queue to be full

There are three important parameters in the thread pool that affect the rejection policy:

  1. CorePoolSize – Number of core threads, which is the minimum number of threads.
  2. WorkQueue – Blocking queue.
  3. MaximumPoolSize – Maximum number of threads If the number of submitted tasks is greater than corePoolSize, the task will be placed in the workQueue blocking queue first. When the blocking queue is saturated, the number of threads in the thread pool is expanded until maximumPoolSize is reached. At this point, additional tasks trigger the thread pool rejection policy. If the number of submitted tasks is greater than (workqueue.size () + maximumPoolSize), the thread pool rejection policy is triggered.

6) Rejection strategy

  1. AbortPolicy: discard task, and throw refuse to enforce RejectedExecutionException exception information. Default reject policy for the thread pool. Exceptions must be handled properly. Otherwise, the current execution process will be interrupted and subsequent tasks will be affected.

  2. CallerRunsPolicy: When the reject policy is triggered, the task is run directly using the calling thread as long as the thread pool is not closed. Generally, concurrency is small and does not require high performance. Failure is not allowed. However, due to the caller running the task, if the task submission speed is too fast, the program may be blocked, resulting in a large loss in performance efficiency

  3. DiscardPolicy: Directly discards cards.

  4. DiscardOldestPolicy: Discards the oldest task in the blocking workQueue and adds a new task to the workQueue if the thread pool is not closed

🧇 Thread pool type

Jav has four default thread pools:

2.1、 newCachedThreadPool

What it does: Creates a cacheable thread pool that has no limit on the size of the thread pool, which depends entirely on the maximum thread size that the operating system (or JVM) can create. If the length of the thread pool exceeds the processing requirements, idle threads can be recycled flexibly, if not, a new thread.

It is ideal for creating an infinitely scalable thread pool with short execution times and multiple tasks.

Features:

  1. The number of threads in the pool is not fixed and can reach (interger.max_value).
  2. Threads in the thread pool can be cache reused and reclaimed (reclaimed by default for 1 minute, i.e. threads with no work for 1 minute).

Creation method:

/** * cacheable thread pool *@return* /
public static ExecutorService newCachedThreadPool(a) {
    /** * corePoolSize Specifies the number of core threads in the thread pool. * maximumPoolSize Specifies the maximum number of threads that can hold. * keepAliveTime Specifies the lifetime of idle threads ThreadFactory Creates a factory class that can omit the handler's rejection policy when the queue is full: it can omit the */
    return new ThreadPoolExecutor(0,
                                  Integer.MAX_VALUE,
                                  60L,
                                  TimeUnit.SECONDS,
                                  new SynchronousQueue<>(),
                                  Executors.defaultThreadFactory(),
                                  new ThreadPoolExecutor.AbortPolicy());
}
Copy the code

2.2, newFixedThreadPool

Function:

To create a reusable thread pool with a fixed number of threads to run as a shared LinkedBlockingQueue.

It is applicable to limit the number of threads in the case of predicting concurrency pressure. Or scenarios with strict limits on the number of threads.

Features:

  1. The number of threads is fixed and reusable.
  2. CorePoolSize and maximunPoolSize set values are equal.
  3. KeepAliveTime is 0. If there are any idle threads, they are stopped immediately.
  4. The blocking queue uses LinkedBlockingQueue, which is an unbounded queue, so it is never possible to reject a task, and any additional tasks must wait in the queue.

Creation method:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
Copy the code

2.3, newSingleThreadExecutor

Function:

Create an Executor that uses a single worker thread and runs it as a LinkedBlockingQueue. Any additional tasks are stored in a queue waiting to be executed.

This is suitable for scenarios where you need to ensure that tasks are executed sequentially and that there are no multiple threads at any point in time

Features:

A maximum of 1 thread is executed in the thread pool, after which the submitted thread activity is queued for execution

Creation method:

public static ExecutorService newSingleThreadExecutor(a) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
Copy the code

2.4, newScheduledThreadPool

Function:

Create a thread pool with corePoolSize as the incoming parameter and a maximum number of threads (integer.max_value) that supports the need to execute tasks regularly and periodically.

More suitable for the need for multiple background threads to perform periodic tasks, some time to collect logs, send push messages to pull and so on are very appropriate 😂

Features:

Thread activities can be timed or deferred (I’ve simulated dynamic timing with this, which means given some future time and then executed at that point) ~~

Creation method:

public static ScheduledExecutorService newScheduledThreadPool(
    int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
    
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue(), threadFactory);
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
Copy the code

🍖 Introduction case

At the railway station, there are three ticket booths for 10 users to buy tickets

import java.util.concurrent.*;

/** ** /
public class ThreadPoolDemo1 {


    /** ** there are 3 tickets at the railway station, 10 users can buy tickets **@param args
     */
    public static void main(String[] args) {
        // The number of timed threads is 3-- the number of Windows is 3
        ExecutorService threadService = new ThreadPoolExecutor(3.3.60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        try {
            // Ten people bought tickets
            for (int i = 1; i <= 10; i++) {
                threadService.execute(() -> {
                    try {
                        System.out.println(Thread.currentThread().getName() + "Window, start selling tickets.");
                        Thread.sleep(500);
                        System.out.println(Thread.currentThread().getName() + "Window tickets closed.");
                    } catch(Exception e) { e.printStackTrace(); }}); }}catch (Exception e) {
            e.printStackTrace();
        } finally {
            // FinishthreadService.shutdown(); }}}/ * * - the thread pool - 1-3 window, began selling tickets - thread pool - 1-2 window, began selling tickets - thread pool - 1-1 window and started selling tickets - thread pool - 1-3 window end - thread pool - 1-2 tickets Pool-1-thread-1 Window ticket purchase end.... * /
Copy the code

🍤 How thread pools work (important)

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
   // If the number of threads in the thread pool is less than corePoolSize, a new thread is created to perform the current task
    if (workerCountOf(c) < corePoolSize) {
        // Execute addworker to create a core thread
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
// If the number of worker threads is greater than the number of core threads, check whether the state of the thread pool is running and can be added to the queue
// If the thread pool is not in the running state, the policy will be rejected. (Addworker will be called once again.)
    if (isRunning(c) && workQueue.offer(command)) {
         // Get the CTL again for a double search
        int recheck = ctl.get();
        // If the thread pool is not in the RUNNING state, the task is removed from the queue.
        // If the removal fails, the worker thread is determined to be zero, and if it is zero, a non-core thread is created
        // If the removal succeeds, the policy is rejected because the thread pool is no longer available;
        if (! isRunning(recheck) && remove(command))
            Call the rejected execution handler for the given command
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null.false);
    }
   // Checks whether new worker threads can be added based on the current pool state and given limits (core or maximum).
   // if so, the number of staff will be adjusted accordingly, and if possible, a new staff will be created and started running firstTask as its firstTask.
   // This method returns false if the pool has been stopped or is eligible to be closed.
   // If the thread factory fails to create a thread when asked, it also returns false.
   // If the Thread creation fails, either because the Thread factory returns NULL or because of an exception (usually an OutOfMemoryError in Thread.start()), we roll back cleanly.
    else if(! addWorker(command,false))
        Call the rejected execution handler for the given command
        reject(command);
}
Copy the code

ThreadPoolExecutor is a thread pool

Execute Execution logic:

  1. After a thread pool is created, the number of threads in the pool is zero
  2. When the execute() method is called to add a request task, the thread pool makes the following judgments:
    • If the number of running threads is less than corePoolSize, create a thread to run the task immediately.
    • If the number of running threads is greater than or equal to corePoolSize, the task is queued;
    • If the queue is full and the number of running threads is less than maximumPoolSize, create a non-core thread to run the task immediately.
    • If the queue is full and the number of running threads is greater than or equal to maximumPoolSize, the thread pool initiates a saturation denial policy to execute.
  3. When a thread completes a task, it takes the next task from the queue and executes it
  4. When a thread has nothing to do for more than a certain amount of time, the thread decides:
    1. If the number of threads currently running is greater than corePoolSize, the thread is stopped.
    2. So after all the tasks in the thread pool are complete, it will eventually shrink to the size of corePoolSize.

🍦 Precautions

It is recommended that you manually create a thread pool using ThreadPoolExecutor and its seven parameters

Why not use Executors to create?

🍕 talk to yourself

Recently, I started to learn JUC again. I feel there is a lot of Java content, but I still think I need to lay a solid foundation in order to go further.

Recently in the continuous update, if you feel helpful to you, also interested in the words, pay attention to me, let us study together, discuss together.

Hello, I am ning Zhichun, a blogger, a small seed on the way of Learning Java. I also hope that one day I can take root and grow into a tree in the sky.

Hope to share with you 😁

We: when we meet, we have achieved something.

Reference:

Implementation principle of ThreadPoolExecutor

www.zhihu.com/question/23… Author: Chai Maomao

Alibaba Development Manual