1. 場景描述
以前多線程也常用,這次因需再頁面上用到多線程,如下圖,總結下,有需要的朋友可以參考下。
2. 解決方案
2.1 線程池概念
線程池官方定義不說了,通俗說下:池子的概念,事先(預定義)創建后,后續的線程可以直接從池子中拿,好處:
(1)來創建線程比較消耗資源,不用重復創建;
(2)池子事先定義好,避免無節制創建線程,導致系統出現不可預測風險。
2.2 創建方式
采用jdk自帶的線程池創建方式,jdk1.5開始提供,在java.util.concurrent 的包下面。
表面上有兩種創建方式,其實一種。
(1)一種是采用new ThreadPoolExecutor進行創建;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
(2)一種是采用Executors.newFixedThreadPool(3),不過還是調用的new ThreadPoolExecutor進行的線程池創建,賦值了幾個默認參數而已。
new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
2.3 參數含義
一共有 7 個參數:
(1)corePoolSize
核心線程數,當有任務進來的時候,如果當前線程數還未達到 corePoolSize 個數,則創建核心線程,核心線程有幾個特點:
1、當線程數未達到核心線程最大值的時候,新任務進來,即使有空閑線程,也不會復用,仍然新建核心線程;2、核心線程一般不會被銷毀,即使是空閑的狀態,但是如果通過方法 allowCoreThreadTimeOut(boolean value) 設置為 true 時,超時也同樣會被銷毀;3、生產環境首次初始化的時候,可以調用 prestartCoreThread() 方法來預先創建所有核心線程,避免第一次調用緩慢;
(2)maximumPoolSize
除了有核心線程外,有些策略是當核心線程完全無空閑的時候,還會創建一些臨時的線程來處理任務,maximumPoolSize 就是核心線程 + 臨時線程的最大上限。臨時線程有一個超時機制,超過了設置的空閑時間沒有事兒干,就會被銷毀。
(3)keepAliveTime
這個就是上面兩個參數里所提到的超時時間,也就是線程的最大空閑時間,默認用於非核心線程,通過 allowCoreThreadTimeOut(boolean value) 方法設置后,也會用於核心線程。
(4)unit
這個參數配合上面的 keepAliveTime ,指定超時的時間單位,秒、分、時等。
(5)workQueue
等待執行的任務隊列,如果核心線程沒有空閑的了,新來的任務就會被放到這個等待隊列中。
(6)threadFactory
它是一個接口,用於實現生成線程的方式、定義線程名格式、是否后台執行等等,可以用 Executors.defaultThreadFactory() 默認的實現即可,也可以用 Guava 等三方庫提供的方法實現,如果有特殊要求的話可以自己定義。它最重要的地方應該就是定義線程名稱的格式,便於排查問題了吧。
(7)handler
當沒有空閑的線程處理任務,並且等待隊列已滿(當然這只對有界隊列有效),再有新任務進來的話,就要做一些取舍了,而這個參數就是指定取舍策略的,有下面四種策略可以選擇:
ThreadPoolExecutor.AbortPolicy:直接拋出異常,這是默認策略;
ThreadPoolExecutor.DiscardPolicy:直接丟棄任務,但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后將新來的任務加入等待隊列
ThreadPoolExecutor.CallerRunsPolicy:由線程池所在的線程處理該任務,比如在 main 函數中創建線程池,如果執行此策略,將有 main 線程來執行該任務
2.4 測試驗證
2.4.1 測試線程
package com.yutong.laowang.test;
public class ThreadTest extends Thread{
@Override
public void run() {
System.out.println("軟件老王:" +Thread.currentThread().getName());
}
}
2.4.2 Executors創建
private static void test4() {
Executor mExecutor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
Thread thread = new ThreadTest();
mExecutor.execute(thread);
}
}
結果:
軟件老王:pool-2-thread-2
軟件老王:pool-2-thread-3
軟件老王:pool-2-thread-1
軟件老王:pool-2-thread-3
軟件老王:pool-2-thread-2
軟件老王:pool-2-thread-1
軟件老王:pool-2-thread-3
軟件老王:pool-2-thread-2
軟件老王:pool-2-thread-3
軟件老王:pool-2-thread-1
2.4.3 ThreadPoolExecutor創建
private static void test3() {
int poolSize = 5;
int queueSize = 100;
ExecutorService executorService = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(queueSize), new ThreadPoolExecutor.AbortPolicy());
for (int i=0;i<10;i++) {
executorService.submit(new ThreadTest());
}
}
結果:
軟件老王:pool-2-thread-1
軟件老王:pool-2-thread-3
軟件老王:pool-2-thread-4
軟件老王:pool-2-thread-2
軟件老王:pool-2-thread-5
軟件老王:pool-2-thread-5
軟件老王:pool-2-thread-1
軟件老王:pool-2-thread-2
軟件老王:pool-2-thread-2
軟件老王:pool-2-thread-1
2.4.4 線程停用
thread.interrupt();
---有時候不一定能執行成功,一般會結合判斷使用,軟件老王,例如:
在ThreadTest類中進行判斷,默認為false,當滿足條件下為true,停用線程。
public volatile boolean exit = false;
I’m 「軟件老王」,如果覺得還可以的話,關注下唄,后續更新秒知!歡迎討論區、同名公眾號留言交流!