學習筆記 2021.10.30續
2021.10.31
併發
執行緒池
執行緒池必考:三大方法、七大引數、四種拒絕策略
池化技術
程式的執行本質即是佔用系統的資源,因此為了優化資源的使用,即引入了池化技術。
池化技術:事先準備好一些資源,需要用的時候就直接拿,用完之後歸還即可。因為資源本身的建立、銷燬等都十分複雜和佔用時間。
執行緒池的好處
- 降低了資源的消耗
- 提高響應速度
- 方便管理
執行緒可複用、可以控制最大併發數、可以管理執行緒
執行緒池的三大方法
即建立執行緒池的三個可呼叫的方法,具體如下:
package JUC; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //執行緒池的三大方法的測試 public class Demo11 { public static void main(String[] args) { ExecutorService ee = Executors.newSingleThreadExecutor();//單個執行緒的執行緒池 // ExecutorService ee = Executors.newFixedThreadPool(5);//建立一個固定大小的執行緒池 // ExecutorService ee = Executors.newCachedThreadPool();//可伸縮的執行緒池,可根據實際執行緒數量進行調整 try { for (int i = 0; i < 10; i++) { ee.execute(() -> { System.out.println(Thread.currentThread().getName()+"ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { ee.shutdown(); } } }
分別的輸出結果如下:
具體的理解就是建立好了執行緒池過後,通過execute方法就可以實現讓池子裡面的執行緒執行對應的run方法的這麼一個過程,而具體用哪些執行緒則就是完全按照上面那三個方法來執行的了。
七大引數
通過看上面三個方法的原始碼可以發現:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
發現其實都是呼叫了ThreadPoolExecutor這個類來實現,而通過再去觀察這個類的底層原始碼可以發現包含了以下七個關鍵引數
所以對於阿里開發手冊:
這麼要求的原因也就可以知道了,即是通過ThreadPoolExecutor能實現所有的方法,通過這樣能夠更好的去理解建立的細節。
最好建立執行緒池和執行緒就用這個方法!!
而對於七個引數的理解可以通過下圖來認識:
具體的對應已經表在圖中了,其中如果都滿了還來人的話,就對應著拒絕策略這個引數。
如果max多出來的幾個執行緒超過一定的時間還沒有使用的話,則對其進行釋放,這即對應著超時等待的引數。
手動建立執行緒池的示例
package JUC; import java.util.concurrent.*; //自定義執行緒池測試 及四大拒絕策略 public class Demo12 { public static void main(String[] args) { ExecutorService ee =new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); try { for (int i = 0; i < 2; i++) { ee.execute(() -> { System.out.println(Thread.currentThread().getName()+"ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { ee.shutdown(); } } }
其中執行後的結果也可以很明確的知道,
當池中容量小於核心加阻塞時,只會按照核心的數量去呼叫。
當池中容量大於核心加阻塞時並且小於最大加阻塞時,則按需開啟池中的數量。
大於最大加阻塞時,則就進行拒絕策略了,這裡的拒絕策略就是超過就報異常。
四種拒絕策略
AbortPolicy
DiscardPolicy
DiscardOldestPolicy
CallerRunsPolicy
其中第二個策略是隊伍滿了過後丟掉任務,但是這個就不會丟擲異常了。
第三個是滿了過後去嘗試和最早的執行緒競爭,這也不會丟擲異常。
第四個理解成從哪來到哪去,當執行緒池內的執行緒不夠處理時,則交給上一級的執行緒去執行,這裡應該一般都是主執行緒。
如下圖所示
package JUC;
import java.util.concurrent.*;
//自定義執行緒池測試 及四大拒絕策略
public class Demo12 {
public static void main(String[] args) {
ExecutorService ee =new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
try {
for (int i = 0; i < 10; i++) {
ee.execute(() -> {
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ee.shutdown();
}
}
}