Write the article as you understand it

1. Learn about Java’s ThreadPoolExecutor

1. The seven parameters of ThreadPoolExecutor

1.int corePoolSize

* @param corePoolSize the number of threads to keep in the pool, even
*        if they are idle, unless {@code allowCoreThreadTimeOut} is set
Copy the code

Number of core threads. If this value is 3, even if there are three idle core threads in the thread pool with no tasks, these three core threads will remain in the thread pool and will not be destroyed. AllowCoreThreadTimeOut (default false) is a Boolean value. If true, the three core threads will also be destroyed because they have been idle for too long.

2.int maximumPoolSize

* @param maximumPoolSize the maximum number of threads to allow in the
*        pool
Copy the code

Maximum number of threads. When none of the core threads are idle and the workqueue is too full, new threads will be created to handle the task. If the total number of threads in the thread pool reaches the maximum number of threads, new threads will not be created

3.long keepAliveTime

* @param keepAliveTime when the number of threads is greater than
*        the core, this is the maximum time that excess idle threads
*        will wait for new tasks before terminating.
Copy the code

In conjunction with parameters 2 and 4

4.TimeUnit unit

* @param unit the time unit for the {@code keepAliveTime} argument
Copy the code

The time unit of the third parameter

5.BlockingQueue<Runnable> workQueue

* @param workQueue the queue to use for holding tasks before they are
*        executed.  This queue will hold only the {@code Runnable}
*        tasks submitted by the {@code execute} method.
Copy the code

Blocking queue, this is just an interface that creates a thread pool by passing in its implementation class, which will put tasks in the queue if the pool has more threads than the core thread.

If the queue is empty, the call to take() is blocked. If the queue is full, the call to put() will block. There are many other ways to block queues

The partial implementation classes are as follows

  1. LinkedBlockingQueue

    Block queue based on a linked list
  2. SynchronousQueue

    A queue with no capacity that blocks when a thread calls put() if no other thread calls take(), and blocks when a thread calls take() if no other thread calls put()
  3. DelayedWorkQueue

    A heap-based blocking queue in which the first tasks to be executed are at the top of the heap, depending on time

6.ThreadFactory threadFactory

* @param threadFactory the factory to use when the executor
*        creates a new thread
Copy the code

A thread factory, which is also an interface, needs to pass in an implementation class for that interface. Use (doubtful), give the thread a name? Thread priority?

7.RejectedExecutionHandler handler

* @param handler the handler to use when execution is blocked
*        because the thread bounds and queue capacities are reached
Copy the code

The rejection policy, which is also an interface, needs to be passed in the implementation class of the interface. The rejection policy executed after the queue is full and the number of threads in the thread pool reaches the maximum number.

Java comes with four rejection policies:

  1. AbortPolicy throws an exception
  2. CallerRunsPolicy lets the caller of the thread pool perform this task
  3. DiscardOldestPolicy Discards the task at the head of the queue and then commits the task again
  4. DiscardPolicy silently discards this task

You can also implement this interface and customize the rejection policy

2. Set the number of threads

Threads sometimes require I/O, such as reading data from disk, or RPC, so they are expected to leave their CPU to other threads while waiting for data.

For example, in the case of CPU1 cores, if 50% of the time a task is computed by the CPU and 50% of the time it needs to wait, you can set two threads.

There are formulas in books and blogs to estimate the number of threads in a thread pool, but the actual number of threads to get the best performance out of a machine needs to be tested.

3. Submit and execute

  1. Public void execute(Runnable command) The execute method can only submit Runnable objects. If an exception occurs during task execution, an error message is displayed

  2. public Future
    submit(Runnable task) If submit(Runnable task) is passed to the thread, future.get() returns null, and future.get() throws an exception. (Note: If future.get() is not called after submit() and the task execution fails, the console will not print any error messages.)

    Future<? > future = pool.submit(() -> { }); System.out.println(future.get());// This will print null
    / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Future<? > future = pool.submit(() -> {int i = 0 / 0;
    });
    future.get();// The get method throws an exception
    Copy the code
  3. Submit (Runnable task, T result) public <T> Future<T> submit(Runnable task, T result

  4. Public

    Future

    submit(Callable

    task) future.get() returns the value of the task on success, future.get() throws an exception on failure


    Future<? > future = pool.submit(() -> {return "lzp";
    });
    System.out.println(future.get());/ / print LZP
    / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Future<? > future = pool.submit(() -> {int i=0/0;
        return "lzp";
    });
    try {
        future.get();
    }catch (Exception e){
        System.out.println("Caught it.");
    }
    // Print the result: catch
    Copy the code

4. Some thread pools provided by Java

Executors.newFixedThreadPool(int nThreads)

Features:

  1. The number of core threads equals the maximum number of threads
  2. The blocking queue is LinkedBlockingQueue<>(integer.max_value), which is large and at risk of running out of memory. (Question: why not set a bottom value to prevent memory overflow?)

Executors.newSingleThreadExecutor()

Features:

  1. The number of core threads is equal to the maximum number of threads and is all 1
  2. The blocking queue is LinkedBlockingQueue<>(integer.max_value), which is large and at risk of running out of memory
  3. What are the advantages over creating your own thread? If the thread pool fails to execute a task, a new thread is created to keep the thread pool running properly

Executors.newCachedThreadPool()

Features:

  1. The number of core threads is 0, and the maximum number of threads is integer.max_value
  2. Threads in the thread pool are destroyed when they are idle for 60 seconds
  3. SynchronousQueue<>(), the SynchronousQueue of the non-fair blocking mode, which is based on a fifO queue to store redundant producers or consumers, or a fifO stack to store redundant producers or consumers.

Executors.newScheduledThreadPool(int corePoolSize)

The three thread pools mentioned above are basically objects of the ThreadPoolExecutor class, with different parameters. This inherits ThreadPoolExecutor with some additional functionality.

Features:

  1. The maximum number of threads is integer. MAX_VALUE. The emergency thread is destroyed when idle

  2. The blocking queue is DelayedWorkQueue()

  3. Delay the execution of the task, pay attention to the details, the delay is not precise, look at the code

    ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
    pool.submit(()->{
        try {
            Thread.sleep(100000000);
        } catch(InterruptedException e) { e.printStackTrace(); }});// It will not print after 1s, because the thread pool has only one thread, and it is executing a very long task at a time, which will affect subsequent tasks
    pool.schedule(()->{
        System.out.println("lzp");
    },1L, TimeUnit.SECONDS);
    Copy the code
  4. There are also apis for periodic tasks that can be used, which can be delayed because the thread pool thread is too busy