1. 程式人生 > >Java中的線程池

Java中的線程池

ict 空閑 con exception 處理流程 線程數 () ima true

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))//創建新的線程,maximumPoolSize 18 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 ctl
22                 if (runStateOf(c) != rs)
23                     continue retry;
24                 // else CAS failed due to workerCount change; retry inner loop
25             }
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、關閉線程池

可以通過調用線程池的shutdownshutdownNow方法來關閉線程池。

原理:遍歷線程池中的工作線程,然後逐個調用線程的interrupt方法來中斷線程,所以無法響應中斷的任務可能永遠無法終止

區別shutdownNow先將線程池的狀態設置成STOP,然後嘗試停止所有正在執行或暫停任務的額線程,並返回等待執行任務的列表;

shutdown只是將線程池的狀態設置成SHUTDOWN狀態,然後中斷所有沒有正在執行任務的線程。

通常調用shutdown來關閉線程池,如果任務不一定要執行完,則可以調用shutdownNow方法。

建議使用有界隊列:能增加系統的穩定性和預警能力。

Java中的線程池