標籤:sub integer 類型 oop led cas logs rup 儲存
1、使用線程池的好處
a、降低資源消耗。通過重複利用已建立的線程降低線程建立和摧毀造成的消耗;
b、提高響應速度。當任務到達時,任務可以不需要等到線程建立就能立即執行;
c、提高線程的可管理性。線程是稀缺資源,如果無限制地建立,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以及進行統一分配、調優和監控。
2、線程池的實現原理
當線程池想提交一個任務之後,線程池的主要處理流程
jdk1.8中ThreadPoolExecutor執行execute()方法的原始碼如下:
1 public void execute(Runnable command) { 2 if (command == null) 3 throw new NullPointerException(); 4 int c = ctl.get();//原子類型AtomicInteger ctl,擷取值 5 if (workerCountOf(c) < corePoolSize) {//判斷當前背景工作執行緒是否小於核心線程數目 6 if (addWorker(command, true))//是則建立新的背景工作執行緒,並將當前任務傳遞進去處理後返回結果,corePoolSize 7 return; 8 c = ctl.get();//如果失敗,繼續執行下面的代碼進行判斷 9 }10 if (isRunning(c) && workQueue.offer(command)) {11 int recheck = ctl.get();12 if (! isRunning(recheck) && remove(command))13 reject(command);14 else if (workerCountOf(recheck) == 0)15 addWorker(null, false);//加入背景工作執行緒16 }17 else if (!addWorker(command, false))//建立新的線程,maximumPoolSize18 reject(command);19 }
注意addWork的第二個參數core的作用:core ? corePoolSize : maximumPoolSize
1 private boolean addWorker(Runnable firstTask, boolean core) { 2 retry: 3 for (;;) { 4 int c = ctl.get(); 5 int rs = runStateOf(c); 6 7 // Check if queue empty only if necessary. 8 if (rs >= SHUTDOWN && 9 ! (rs == SHUTDOWN &&10 firstTask == null &&11 ! workQueue.isEmpty()))12 return false;13 14 for (;;) {15 int wc = workerCountOf(c);16 if (wc >= CAPACITY ||17 wc >= (core ? corePoolSize : maximumPoolSize))18 return false;19 if (compareAndIncrementWorkerCount(c))20 break retry;21 c = ctl.get(); // Re-read ctl22 if (runStateOf(c) != rs)23 continue retry;24 // else CAS failed due to workerCount change; retry inner loop25 }26 }
線程池中的線程執行任務分為兩種情況:
a、在execute()方法中建立一個線程時,會讓這個線程執行當前任務;
b、在這個線程執行完上述任務後,會反覆從BlockingQueue擷取任務來執行。
3、線程池的使用
我們可以通過ThreadPoolExecutor來建立一個線程池。
new ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
corePoolSize:線程池的基本大小,當提交一個任務到線程池時,線程池會建立一個線程來執行任務,即使其他閒置基本線程能夠執行新任務也會建立線程,等到需要執行的任務數大於線程池基本大小時就不再建立。如果調用了線程池的prestartAllCoreThreads()方法,線程池會提前建立並啟動所有基本線程。
maximumPoolSize:線程池的最大數量,線程池允許建立的最大線程數。如果隊列滿了,並且已建立的線程數小於最大線程數,則線程池會再建立新的線程執行任務。值得注意的是,如果使用了無界的任務隊列這個參數就沒什麼效果。
KeepAliveTime:線程活動保持時間,線程池的背景工作執行緒空閑後,保持存活的時間。
unit:線程活動保持時間的單位,可選的單位有天,小時,分鐘,毫秒,微秒,納秒。
workQueue:任務隊列,用來儲存等待執行的任務的阻塞隊列。在任務被執行之前儲存這些任務,工作隊列僅儲存執行了execute方法的任務。可以選擇以下幾個阻塞隊列:ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue和PriorityBlockingQueue。
threadFactory:用於設定建立線程的工廠,可以通過線程工廠給每個建立出來的線程設定更有意義的名字。
handler:飽和策略,當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採取一種策略處理提交的新任務。這個策略預設情況下是AbortPolicty,表示無法處理新任務時拋出異常。
4、向線程池提交任務
可以使用兩個方法向線程池提交任務,分別是execute()和submit()方法。
execute()方法用於提交不需要傳回值的任務,所以無法判斷任務是否被線程池執行成功。
submit()方法用於提交需要傳回值的任務。線程池會返回一個future類型的對象,通過這個future對象可以判斷任務是否執行成功,並且可以通過future的get()方法來擷取傳回值,get()方法會阻塞當前線程知道任務完成,而使用get(long timeout, TimeUnit unit)方法則會阻塞當前線程一段時間後立即返回,這時候可能任務還沒有執行完。
5、關閉線程池
可以通過調用線程池的shutdown或shutdownNow方法來關閉線程池。
原理:遍曆線程池中的背景工作執行緒,然後逐個調用線程的interrupt方法來中斷線程,所以無法響應中斷的任務可能永遠無法終止。
區別:shutdownNow先將線程池的狀態設定成STOP,然後嘗試停止所有正在執行或暫停任務的額線程,並返回等待執行任務的列表;
shutdown只是將線程池的狀態設定成SHUTDOWN狀態,然後中斷所有沒有正在執行任務的線程。
通常調用shutdown來關閉線程池,如果任務不一定要執行完,則可以調用shutdownNow方法。
建議使用有界隊列:能增加系統的穩定性和預警能力。
Java中的線程池