Java multithreading-6-thread pool

Six, thread pool

1. Use advantages

  1. Reduce resource consumption

    You can reuse created threads to reduce the consumption caused by thread creation and destruction

  2. Improve response speed

    When the task arrives, it can be executed immediately without waiting for the thread to be created

  3. Improve thread manageability

    Thread is a scarce resource, creating infinitely will not only consume system resources, but also reduce system stability. The use of thread pools can be used for uniform allocation, tuning and monitoring

2. The creation of thread pool

By java.util.concurrent.ThreadPoolExecutorcreating

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    
     }

corePoolSize: the number of core threads

maximumPoolSize: the maximum number of threads (including the number of core threads)

keepAliveTime: The survival time (numerical value) of non-core threads. The maximum time for non-core threads (idle state) to wait for the termination of new tasks (wait for new tasks before terminating)java.lang.Thread.State: WAITING (parking)

unit: the survival time of non-core threads (unit)

workQueue: thread task queue

threadFactory: Method factory for generating threads. For example, the thread factory created using the default static methodjava.util.concurrent.Executors#defaultThreadFactory

handler: Thread task rejection strategy. For example, use the default deny execution strategyjava.util.concurrent.ThreadPoolExecutor.AbortPolicy

/* 会直接抛出异常 */
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
    throw new RejectedExecutionException("Task " + r.toString() +
                                         " rejected from " +
                                         e.toString());
}

3. Working method

  1. When executing a thread task, first determine whether the number of core threads (corePoolSize) is reached, if not reached, a new thread will be created to execute the thread task, otherwise the thread task will be added to the blocking queue (BlockingQueue)
  2. When the blocking queue is full and the maximum number of threads (maximumPoolSize) has not been reached, a new thread will be created to perform thread tasks
  3. When the maximum number of threads is reached, if thread tasks need to be executed, the rejection strategy (handler) will be executed
  4. When additional threads (non-core threads) are idle and the maximum waiting time is reached, they will be terminated ( TERMINATED )

Note: When creating a new thread, you need to acquire a global lock

/**
 * Lock held on access to workers set and related bookkeeping.
 * While we could use a concurrent set of some sort, it turns out
 * to be generally preferable to use a lock. Among the reasons is
 * that this serializes interruptIdleWorkers, which avoids
 * unnecessary interrupt storms, especially during shutdown.
 * Otherwise exiting threads would concurrently interrupt those
 * that have not yet interrupted. It also simplifies some of the
 * associated statistics bookkeeping of largestPoolSize etc. We
 * also hold mainLock on shutdown and shutdownNow, for the sake of
 * ensuring workers set is stable while separately checking
 * permission to interrupt and actually interrupting.
 */
private final ReentrantLock mainLock = new ReentrantLock();


private boolean addWorker(Runnable firstTask, boolean core) {
    
    

    // ... ...

    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;
}

Although you can use concurrency, it is best to use locks. Because on the one hand, thread safety can be ensured, so that the thread pool runs stably; on the other hand, some operations are simplified (thread pool source code implementation)

4. Best practices

  1. Reasonably configure the maximum number of threads
    • CPU-intensive tasks: Number of CPU cores + 1 [Reduce thread switching]
    • IO-intensive tasks [calculation method one]: CPU core number × 2
    • IO-intensive tasks [calculation method two]: CPU core number / (1-blocking coefficient), the blocking coefficient is between 0.8 ~ 0.9 [Increase throughput]
  2. Properly configure the keepAliveTime of non-core threads. If there are many tasks to be executed and the task execution time is short, you can increase this time to improve thread utilization
  3. Configure the size of the blocking queue reasonably. If it is too large, not only the task cannot be executed, but also the memory resources may be exhausted
  4. There are two ways shutdownto close the thread pool . It is only to close the thread pool, but the unfinished tasks will still be executed; it shutdownNowis to close the thread pool, but the unfinished tasks must be terminated.

Guess you like

Origin blog.csdn.net/adsl624153/article/details/103865353