1. 程式人生 > 其它 >Java基礎知識24--ThreadPoolExecutor執行緒池詳細使用以及Java VisualVM監控執行緒使用情況

Java基礎知識24--ThreadPoolExecutor執行緒池詳細使用以及Java VisualVM監控執行緒使用情況

1.ThreadPoolExecutor概述

《阿里巴巴 Java 開發手冊》中強制執行緒池不允許使用 Executors 去建立,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險;

Executors 返回執行緒池物件的弊端如下:

  • FixedThreadPool 和 SingleThreadExecutor : 允許請求的佇列長度為 Integer.MAX_VALUE ,可能堆積大量的請求,從而導致 OOM。

  • CachedThreadPool 和 ScheduledThreadPool : 允許建立的執行緒數量為 Integer.MAX_VALUE ,可能會建立大量執行緒,從而導致 OOM。

ThreadPoolExecutor 是 JDK 中的執行緒池實現,這個類實現了一個執行緒池需要的各個方法,它實現了任務提交、執行緒管理、監控等等方法。

建立執行緒池主要是 ThreadPoolExecutor 類來完成。

1.1ThreadPoolExecutor類的構造方法

ThreadPoolExecutor 類的構造方法:

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

ThreadPoolExecutor中3 個最重要的引數:

(1) corePoolSize :核心執行緒數,定義了最小可以同時執行的執行緒數量。
(2) maximumPoolSize: 執行緒不夠用時能夠建立的最大執行緒數。
(3) workQueue: 當新任務來的時候會先判斷當前執行的執行緒數量是否達到核心執行緒數,如果達到的話,新任務就會被存放在佇列中。

(4) keepAliveTime:當執行緒池中的執行緒數量大於 `corePoolSize` 的時候,如果這時沒有新的任務提交,核心執行緒外的執行緒不會立即銷燬,而是會等待,直到等待的時間超過了 keepAliveTime才會被回收銷燬;
(5) unit : keepAliveTime 引數的時間單位。

1.2 執行緒池的工作過程

(1)執行緒池剛建立時,裡面沒有一個執行緒,任務佇列作為引數傳入。此時,即使任務佇列中有任務,執行緒池也不會馬上執行他們。

(2)當呼叫 execute() 方法新增一個任務時,執行緒池會做如下判斷:

  • 如果正在執行的執行緒數 < corePoolSize,那麼建立執行緒執行任務;

  • 如果正在執行的執行緒數 >= corePoolSize,那麼將任務放入任務佇列;

  • 如果佇列滿了,並且正在執行的執行緒數 < maximumPoolSize,那麼將建立非核心執行緒執行任務;

  • 如果佇列滿了,並且正在執行的執行緒數 >= maximumPoolSize,那麼執行緒池會丟擲異常 RejectExecutionExcaption

(3)當一個執行緒完成任務後,在從佇列中取出一個任務來執行

(4)如果一個執行緒空閒超過一定時間(keepAlivTime),執行緒池會判斷,如果正在執行的執行緒數 > corePoolSize,則回收該執行緒。執行緒池在任務執行完後,執行緒數會維持在 corePoolSize 的大小。

2.ThreadPoolExecutor使用案例

ThreadPoolController.java

/**
 * @Author lucky
 * @Date 2022/3/1 16:48
 */
@Slf4j
@RestController
@RequestMapping("/testThreadPool")
public class ThreadPoolController {
    private static final int CORE_POOL_SIZE=500;
    private static final int MAX_POOL_SIZE=600;
    private static final int QUEUE_CAPACITY=600;
    private static final Long KEEP_ALIVE_TIME=1L;

    @PostMapping("/uploadFileInner")
    public void uploadFileInner(@RequestParam int threadNum){
        //01 使用ThreadPoolExecutor建立執行緒池
        ThreadPoolExecutor executor=new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<>(QUEUE_CAPACITY));

        //02 建立指定threadNum個數的執行緒,利用CountDownLatch模擬併發
        final CountDownLatch start=new CountDownLatch(threadNum);
        for (int i = 0; i <threadNum ; i++) {
            ConcurrentUploadThread concurrentUploadThread=new ConcurrentUploadThread(start);
            executor.execute(concurrentUploadThread);
        }
        //03 關閉執行緒池
        executor.shutdown();
    }
}

ConcurrentUploadThread.java

/**
 * @Author lucky
 * @Date 2022/3/1 17:22
 */
@Slf4j
public class ConcurrentUploadThread implements Runnable {
    private final CountDownLatch startSignal;

    public ConcurrentUploadThread(CountDownLatch startSignal){
        this.startSignal=startSignal;
    }

    @Override
    public void run() {
        startSignal.countDown();
        log.info(Thread.currentThread().getName()+",prepare at:"+System.currentTimeMillis());
        try {
            startSignal.await();
            doTask();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void doTask() {
        try {
            Random random=new Random();
            int temp = random.nextInt(500);
            Thread.sleep(2000+temp);
            log.info(Thread.currentThread().getName()+"...............");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Postman測試:

參考文獻:

https://blog.csdn.net/qielanyu_/article/details/115614762