摘要:中的線程池是運用場景最多的并發框架。才是真正的線程池。存放任務的隊列存放需要被線程池執行的線程隊列。所以線程池的所有任務完成后,它最終會收縮到的大小。飽和策略一般情況下,線程池采用的是,表示無法處理新任務時拋出異常。
Java線程池 1. 簡介
系統啟動一個新線程的成本是比較高的,因為它涉及與操作系統的交互,這個時候使用線程池可以提升性能,尤其是需要創建大量聲明周期很短暫的線程時。Java中的線程池是運用場景最多的并發框架。
線程池類似于數據庫連接池,在系統啟動的時候即創建大量空閑的線程,可以將一個線程任務提交給線程池執行,當任務執行完后,線程不會死亡,而是再次返回線程池中成為空閑狀態。
使用線程池的好處降低資源消耗:重復利用降低創建和銷毀線程的消耗;
提高響應速度:任務來了可以理解執行,不必等待線程創建;
提高線程的可管理性:使用線程池可以統一的分配、調優和監控。
2. 使用Executors工廠類來產生線程Executors工廠類主要有4種方式生產線程池:
1.newCachedThreadPool() :創建一個具有緩存功能的線程池,線程池無邊界,適用于執行很多短期異步任務的小程序,或者負載較輕的服務器。
2. newFixedThreadPool(int nThreads):創建一個固定線程數的線程池,適用于為了滿足資源管理的要求而需要限制線程數量的場景,比如負載較重的服務器。
3. newSingleThreadExecutor():創建一個只有單線程的線程池,適用于需要保證順序地執行各個任務,并且在任意時間點不會有多個線程是活動的西場景。
4. newScheduledThreadPool(int corePoolSize):創建一個具有指定線程數的線程池,并可以在指定延遲后執行線程任務,適用于多個后臺線程執行周期任務,同時為了滿足資源管理需要限制線程數量的場景。
實例代碼package com.wangjun.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /* * 演示線程池的使用 */ public class ThreadPoolTest { public static void main(String[] args) throws InterruptedException, ExecutionException { //生產普通的線程池 ExecutorService threadPool = Executors.newFixedThreadPool(6); // 第一種執行線程的方式 threadPool.submit(new MyThread()); // 第二種執行線程的方式,有返回值 Future3. 線程池類ThreadPoolExectorresult = threadPool.submit(new MyThread(), "返回值1"); System.out.println(result.get()); // 第三種執行線程的方式,傳入Callable對象,有返回值 Future result2 = threadPool.submit(new MyThread2()); System.out.println(result2.get()); // 關閉線程池,不再接受新的任務,會將之前所有提交的任務執行完成,所有任務完成后,所有的線程死亡 // 調用shutdownNow可以立馬停止所有線程 threadPool.shutdown(); System.out.println("-----"); // 生產可以延遲執行的線程池 ScheduledExecutorService threadPool2 = Executors.newScheduledThreadPool(6); // 延遲1秒執行 threadPool2.schedule(new MyThread(), 1, TimeUnit.SECONDS); // 延遲2秒后執行,每一秒循環執行一次 // 是以上一個任務開始的時間計時,period時間過去后,檢測上一個任務是否執行完畢,如果上一個任務執行完畢, // 則當前任務立即執行,如果上一個任務沒有執行完畢,則需要等上一個任務執行完畢后立即執行。 threadPool2.scheduleAtFixedRate(new MyThread(), 2, 1, TimeUnit.SECONDS); // 是以上一個任務結束時開始計時,period時間過去后,立即執行。 // 兩個方法以不同的時間點作為參考 threadPool2.scheduleWithFixedDelay(new MyThread(), 2, 1, TimeUnit.SECONDS); Thread.sleep(3000); threadPool2.shutdown(); } static class MyThread extends Thread { @Override public void run() { System.out.println("線程:" + Thread.currentThread().getName()); } } static class MyThread2 implements Callable { @Override public String call() throws Exception { System.out.println("線程:" + Thread.currentThread().getName()); return "返回值2"; } } }
上面使用Executors工廠類生產線程池,其實大部分返回的就是ThreadPoolExector的實例。ThreadPoolExector才是真正的線程池。
3.1 線程池的幾個關鍵屬性核心線程數量corePoolSize:核心線程數,指保留的線程池大?。ú怀^maximumPoolSize值時,線程池中最多有corePoolSize 個線程工作)。
最大線程數量maximumPoolSize:指的是線程池的最大大小,使用使用了無界隊列,那這個參數就沒什么用。
線程結束的超時時間keepAliveTime:當一個線程不工作時,過keepAliveTime時間將停止該線程(該線程是多余的,指大于corePoolSize的那些線程)。
存放任務的隊列workQueue:存放需要被線程池執行的線程隊列。
飽和策略handler:加任務失敗后如何處理該任務。通常是AbortPolicy,表示無法處理新任務時拋出異常。
3.2 線程池執行策略線程池執行execute()方法的過程如圖:
線程池剛創建時,里面沒有一個線程。任務隊列是作為參數傳進來的。不過,就算隊列里面有任務,線程池也不會馬上執行它們。
當調用 execute() 方法添加一個任務時,線程池會做如下判斷:
? a. 如果正在運行的線程數量小于 corePoolSize,則創建線程執行任務(注意,這一步需要獲取全局鎖);
? b. 如果正在運行的線程數量大于或等于 corePoolSize并且隊列沒有滿,那么將這個任務放入隊列。
? c. 如果這時候隊列滿了,而且正在運行的線程數量小于 maximumPoolSize,那么還是要創建線程運行這個任務;
? d. 如果隊列滿了,而且正在運行的線程數量大于或等于 maximumPoolSize,那么線程池會交給配置的執行策略處理(調用RejectedExecutionHandler.rejectedExecution方法),比如拋出異常,提示無法加入新線程。
當一個線程完成任務時,它會從隊列中取下一個任務來執行。
當一個線程無事可做,超過一定的時間(keepAliveTime)時,線程池會判斷,如果當前運行 的線程數大于 corePoolSize,那么這個線程就被停掉。所以線程池的所有任務完成后,它最終會收縮到 corePoolSize 的大小。
3.3 線程池中阻塞隊列的種類ArrayBlockingQueue:居于數組結構的有界阻塞隊列,按照先進先出FIFO原則對元素進行排序;
LinkedBlockingQueue:基于鏈表結構的阻塞隊列,按照FIFO排序原色,吞吐量大于ArrayBlockingQueue,靜態工廠方法Executors.newFixedThreadPool()就使用了這個隊列;
SynchronousQueue:不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處于阻塞狀態,吞吐量通常要高于LinkedBlockingQueue,靜態工程方法Executors.newCachedThreadPool()就使用了這個隊列;
PriorityBlockingQueue:具有優先級的無限阻塞隊列。
3.4 飽和策略一般情況下,線程池采用的是AbortPolicy,表示無法處理新任務時拋出異常。JDK1.5中java線程池框架提供了4種飽和策略:
AbortPolicy:直接拋出異常;
CallerRunsPolicy:使用調用者所在線程來運行任務;
DiscardOldestPolicy:丟棄隊列里最近的一個任務,并執行當前任務;
DiscardPolicy:不處理,丟棄掉。
也可以根據場景來實現RejectedExecutionHandler接口來自定義策略。
3.5 向線程池提交任務execute:用于提交不需要返回值的任務,因此無法判斷任務是否被線程執行成功;
submit:用于提交需要有返回值的任務,返回一個Future對象,通過get()獲取返回值,會阻塞當前線程直到任務完成。
3.6 關閉線程池通過調用線程池的shutdown和shutdownNow方法來關閉線程池,他們的原理是遍歷線程池中的工作線程,然后逐個調用線程的interrupt方法來中斷線程,所以無法影響中斷的任務可能永遠無法終止。
這兩個方法的區別是shutdownNow首先將線程池的狀態設置為STOP,然后嘗試停止所有的正在執行或暫停任務的線程,并返回等待任務的列表,而shutdown只是將線程池的狀態設置為SHUTDOWN狀態,然后中斷所有沒有正在執行任務的線程。
只要調用了這兩個關閉方法中的任意一個,isShutdown方法就會返回true,當所有的任務都已關閉后,才表示線程池關閉成功,這時調用isTerminaed方法會返回true。通常調用shutdown來關閉線程池,如果任務不一定要執行完,則可以調用shutdownNow方法。
4. Executor框架JDK5之前,java的線程Thread既是工作單元,也是執行單元(start方法執行),JDK5開始,把工作單元與執行機制分離開來,工作單元包括Runnable和Callable,而執行機制由Executor框架提供。(注意這里的Executor和Executors線程池工廠類不是一回事。)
線程池類ThreadPoolExecutor的繼承關系:
ThreadPoolExecutor -> AbstractExecutorService -> ExecutorService(接口) -> Executor(接口)。
可以看到ThreadPoolExecutor就是Executor接口的一個實現方法。
4.1 Executor的兩層調度模型傳統的java線程(java.lang.Thread)在JVM中被一對一映射為本地操作系統線程,java線程啟動時會創建一個本地操作系統線程,當java線程終止時,這個操作系統線程也會被回收。
而在Executor框架中,將用戶任務映射為固定數量的線程,在底層操作系統內核將這些線程映射到硬件處理器上,應用程序通過Executor框架控制上層調度,下層的調度由操作系統內核控制。
4.2 Executor框架的結構Executor框架主要有3部分組成:
任務:包括被執行任務需要實現的接口:Runnable和Callable;
任務的執行:核心接口是Executor,繼承Executor的ExecutorService接口。ExecutorService接口有兩個核心實現類,ThreadPoolExecutor和ScheduledThreadPoolExecutor(繼承ThreadPoolExecutor,可以延遲執行任務,比Timer更強大)。
異步計算的結果:包括接口Future和實現Future接口的FutureTask類。
來看一下Executor框架中主要類和接口的UML圖:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69629.html
摘要:當活動線程核心線程非核心線程達到這個數值后,后續任務將會根據來進行拒絕策略處理。線程池工作原則當線程池中線程數量小于則創建線程,并處理請求。當線程池中的數量等于最大線程數時默默丟棄不能執行的新加任務,不報任何異常。 spring-cache使用記錄 spring-cache的使用記錄,坑點記錄以及采用的解決方案 深入分析 java 線程池的實現原理 在這篇文章中,作者有條不紊的將 ja...
摘要:四種線程池的使用介紹的弊端及四種線程池的使用,線程池的作用線程池作用就是限制系統中執行線程的數量。相比,提供的四種線程池的好處在于重用存在的線程,減少對象創建消亡的開銷,性能佳。延遲執行描述創建一個定長線程池,支持定時及周期性任務執行。 java 四種線程池的使用 介紹new Thread的弊端及Java四種線程池的使用 1,線程池的作用 線程池作用就是限制系統中執行線程的數量。 ...
摘要:高并發系列第篇文章。簡單的說,在使用了線程池之后,創建線程變成了從線程池中獲取一個空閑的線程,然后使用,關閉線程變成了將線程歸還到線程池。如果調用了線程池的方法,線程池會提前把核心線程都創造好,并啟動線程池允許創建的最大線程數。 java高并發系列第18篇文章。 本文主要內容 什么是線程池 線程池實現原理 線程池中常見的各種隊列 自定義線程創建的工廠 常見的飽和策略 自定義飽和策略 ...
閱讀 3073·2021-11-19 09:40
閱讀 1558·2021-11-15 11:39
閱讀 661·2021-10-08 10:05
閱讀 2272·2021-09-03 10:29
閱讀 3405·2021-08-12 13:22
閱讀 2150·2019-08-30 15:54
閱讀 3706·2019-08-30 14:03
閱讀 2653·2019-08-30 13:45