电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

詳細分析JAVA 線程池

瀏覽:44日期:2022-08-30 16:43:49

系統啟動一個新線程的成本是比較高的,因為它涉及與操作系統交互。在這種情形下,使用線程池可以很好地提高性能,尤其是當程序中需要創建大量生存期很短暫的線程時,更應該考慮使用線程池。

與數據庫連接池類似的是,線程池在系統啟動時即創建大量空閑的線程,程序將一個 Runnable 對象或 Callable 對象傳給線程池,線程池就會啟動一個線程來執行它們的 run() 或 call() 方法,當 run() 或 call() 方法執行結束后,該線程并不會死亡,而是再次返回線程池成為空閑狀態,等待執行下一個 Runnable 對象的 run() 或 call() 方法。

除此之外,使用線程池可以有效地控制系統中并發線程的數量,當系統中包含大量并發線程時,會導致系統性能劇烈下降,甚至導致 JVM 崩潰,而線程池的最大線程數參數可以控制系統中并發線程數不超過此數。

Java 8 改進的線程池

在 Java 5 以前,開發者必須手動實現自己的線程池;從 Java 5 開始, Java 內建支持線程池。 Java 5 新增了一個 Executors 工廠類來產生線程池,該工廠類包含如下幾個靜態工廠方法來創建線程池。

newCachedThreadPool():創建一個具有緩存功能的線程池,系統根據需要創建線程,這些線程將會被緩存在線程池中。 newFixedThreadPool(int nThreads):創建一個可重用的、具有固定線程數的線程池。 newSingleThreadExecutor():創建一個只有單線程的線程池,它相當于調用 newFixedThreadPool() 方法時傳入參數為1。 newScheduledThreadPool(int corePoolSize):創建具有指定線程數的線程池,它可以在指定延遲后執行線程任務。 corePoolSize 指池中所保存的線程數,即使線程是空閑的也被保存在線程池內。 newSingleThreadScheduledExecutor():創建只有一個線程的線程池,它可以在指定延遲后執行線程任務。 ExecutorService newWorkStealingPool(int parallelism):創建持有足夠的線程的線程池來支持給定的并行級別,該方法還會使用多個隊列來減少競爭。 ExecutorService newWorkStealingPool():該方法是前一個方法的簡化版本。如果當前機器有 4 個CPU, 則目標并行級別被設置為 4,也就是相當于為前一個方法傳入 4 作為參數。

上面7個方法中的前三個方法返回一個 ExecutorService 對象,該對象代表一個線程池,它可以執行 Runnable 對象或 Callable 對象所代表的線程;而中間兩個方法返回一個 ScheduledExecutorService 線程池,它是 ExecutorService 的子類,它可以在指定延遲后執行線程任務;最后兩個方法則是 Java 8 新增的,這兩個方法可充分利用多 CPU 并行的能力。這兩個方法生成的 work stealing 池,都相當于后臺線程池,如果所有的前臺線程都死亡了,work stealing 池中的線程會自動死亡。

由于目前計算機硬件的發展日新月異,即使普通用戶使用的電腦通常也都是多核 CPU,因此 Java 8 在線程支持上也增加了利用多 CPU 并行的能力,這樣可以更好地發揮底層硬件的性能。

ExecutorService 代表盡快執行線程的線程池(只要線程池中有空閑線程,就立即執行線程任務),程序只要將一個 Runnable 對象或 Callable 對象(代表線程任務)提交給該線程池,該線程池就會盡快執行該任務。 ExecutorService 里提供了如下三個方法。

Future <?> submit(Runnable task):將一個 Runnable 對象提交給指定的線程池,線程池將在有空閑線程時執行 Runnable 對象代表的任務。其中 Future 對象代表 Runnable 任務的返回值,但 run() 方法沒有返回值,所以 Future 對象將在 run() 方法執行結束后返回 null 。但可以調用 Future 的 isDone()、 isCancelled() 方法來獲得 Runnable 對象的執行狀態。 <T> Future <T> submit(Runnable task, T result):將一個 Runnable 對象提交給指定的線程池,線程池將在有空閑線程時執行 Runnable 對象代表的任務。其中 result 顯式指定線程執行結束后的返回值,所以 Future 對象將在 run() 方法執行結束后返回 result 。 <T> Future <T> submit(Callable <T> task):將一個 Callable 對象提交給指定的線程池,線程池將在有空閑線程時執行 Callable 對象代表的任務。其中 Future 代表 Callable 對象里 call() 方法的返回值。

ScheduledExecutorService 代表可在指定延遲后或周期性地執行線程任務的線程池,它提供了如下4個方法。

ScheduledFuture<V> schedule (Callable<V> callable, long delay, TimeUnit unit):指定 callable 任務將在 delay 延遲后執行。 ScheduledFuture<?> schedule(Runnable command , long delay , TimeUnit unit):指定 command 任務將在 delay 延遲后執行。 ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period , TimeUnit unit) : 指定 command 任務將在 delay 延遲后執行,而且以設定頻率重復執行。也就是說,在 initialDelay 后開始執行,依次在 initialDelay + period 、 initialDelay +2* period …處重復執行,依此類推。 ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):創建并執行一個在給定初始延遲后首次啟用的定期操作,隨后在每一次執行終止和下一次執行開始之間都存在給定的延遲。如果任務在任一次執行時遇到異常,就會取消后續執行;否則,只能通過程序來顯式取消或終止該任務。

用完一個線程池后,應該調用該線程池的 shutdown() 方法,該方法將啟動線程池的關閉序列,調用 shutdown() 方法后的線程池不再接收新任務,但會將以前所有已提交任務執行完成。當線程池中的所有任務都執行完成后,池中的所有線程都會死亡;另外也可以調用線程池的 shutdownNow() 方法來關閉線程池,該方法試圖停止所有正在執行的活動任務,暫停處理正在等待的任務,并返回等待執行的任務列表。

使用線程池來執行線程任務的步驟如下。

調用 Executors 類的靜態工廠方法創建一個 ExecutorService 對象,該對象代表一個線程池。 創建 Runnable 實現類或 Callable 實現類的實例,作為線程執行任務。 調用 ExecutorService 對象的 submit() 方法來提交 Runnable 實例或 Callable 實例。 當不想提交任何任務時,調用 ExecutorService 對象的 shutdown() 方法來關閉線程池。

下面程序使用線程池來執行指定 Runnable 對象所代表的任務。

//實現Runnable接口來定義一個簡單的class TestThread implements Runnable{ public void run(){ for (int i = 0; i < 100 ; i++ ){ System.out.println(Thread.currentThread().getName() + '的i值為:' + i); } }}public class ThreadPoolTest{ public static void main(String[] args) { //創建一個具有固定線程數(6)的線程池 ExecutorService pool = Executors.newFixedThreadPool(6); //向線程池中提交2個線程 pool.submit(new TestThread()); pool.submit(new TestThread()); //關閉線程池 pool.shutdown(); }}

上面程序中創建 Runnable 實現類與最開始創建線程池并沒有太大差別,創建了 Runnable 實現類之后程序沒有直接創建線程、啟動線程來執行該 Runnable 任務,而是通過線程池來執行該任務,使用線程池來執行 Runnable 任務的代碼如程序中粗體字代碼所示。運行上面程序,將看到兩個線程交替執行的效果,如下圖所示。

詳細分析JAVA 線程池

Java 8 增強的 ForkJoinPool

現在計算機大多已向多 CPU 方向發展,即使普通 PC ,甚至小型智能設備(如手機)、多核處理器也已被廣泛應用。在未來的日子里,處理器的核心數將會發展到更多。

雖然硬件上的多核 CPU 已經十分成熟,但很多應用程序并未為這種多核 CPU 做好準備,因此并不能很好地利用多核 CPU 的性能優勢。

為了充分利用多 CPU 、多核 CPU 的性能優勢,計算機軟件系統應該可以充分“挖掘”每個 CPU 的計算能力,絕不能讓某個 CPU 處于“空閑”狀態。為了充分利用多 CPU 、多核 CPU 的優勢,可以考慮把一個任務拆分成多個“小任務”,把多個“小任務”放到多個處理器核心上并行執行;當多個“小任務”執行完成之后,再將這些執行結果合并起來即可。

Java 7 提供了 ForkJoinPool 來支持將一個任務拆分成多個“小任務”并行計算,再把多個“小任務”的結果合并成總的計算結果。 ForkJoinPool 是 ExecutorService 的實現類,因此是一種特殊的線程池。ForkJoinPool 提供了如下兩個常用的構造器。

ForkJoinPool(int parallelism):創建一個包含 parallelism 個并行線程的 ForkJoinPool 。 ForkJoinPool():以 Runtime.availableProcessors() 方法的返回值作為 parallelism 參數來創建 ForkJoinPool

Java 8 進一步擴展了 ForkJoinPool 的功能 ,Java 8 為 ForkJoinPool 增加了通用池功能。 ForkJoinPool 類通過如下兩個靜態方法提供通用池功能。

ForkJoinPool commonPool():該方法返回一個通用池,通用池的運行狀態不會受 shutdown() 或 shutdownNow() 方法的影響。當然,如果程序直接執行 System.exit(0); 來終止虛擬機,通用池以及通用池中正在執行的任務都會被自動終止。 int getCommonPoolParallelism():該方法返回通用池的并行級別。

創建了 ForkJoinPool 實例之后,就可調用 ForkJoinPool 的 submit(ForkJoinTask task) 或 invoke(ForkJoinTask task) 方法來執行指定任務了。其中 ForkJoinTask 代表一個可以并行、合并的任務。ForkJoinTask 是一個抽象類,它還有兩個抽象子類 : RecursiveAction 和 RecursiveTask 。其中 RecursiveTask 代表有返回值的任務,而 RecursiveAction 代表沒有返回值的任務。

下面以執行沒有返回值的“大任務”(簡單地打印0〜300的數值)為例,程序將一個“大任務”拆分成多個“小任務”,并將任務交給 ForkJoinPool 來執行。

import java.util.concurrent.ForkJoinPool;import java.util.concurrent.RecursiveAction;import java.util.concurrent.TimeUnit;class PrintTask extends RecursiveAction{ // 每個“小任務”最多只打印50個數 private static final int THRESHOLD = 50; private int start; private int end; // 打印從 start 到 end 的任務 public PrintTask(int start, int end) { this.start = start; this.end = end; } @Override protected void compute() { // 當 end 與 start 之間的差小于 THRESHOLD 時,開始打印 if(end-start<THRESHOLD) { for(int i=start;i<end;i++) { System.out.println(Thread.currentThread().getName()+'的 i 值:'+i); } }else { // 當 end 與 start 之間的差大于 THRESHOLD 時,即要打印的數超過50個時 // 將大任務分解成兩個“小任務” int middle = (start+end)/2; PrintTask left = new PrintTask(start, middle); PrintTask right = new PrintTask(middle, end); // 并行執行兩個“小任務” left.fork(); right.fork(); } }}public class ForkJoinPoolTest { public static void main(String[] args) throws Exception { ForkJoinPool pool = new ForkJoinPool(); // 提交可分解的 PrintTask 任務 pool.submit(new PrintTask(0, 300)); pool.awaitTermination(2, TimeUnit.SECONDS); // 關閉線程池 pool.shutdown(); }}

上面程序中的粗體字代碼實現了對指定打印任務的分解,分解后的任務分別調用 fork() 方法開始并行執行。運行上面程序,可以看到如下圖所示的結果。

詳細分析JAVA 線程池

從如上圖所示的執行結果來看, ForkJoinPool 啟動了 4個線程來執行這個打印任務——這是因為測試計算機的 CPU 是4核的。不僅如此,讀者可以看到程序雖然打印了 0〜299這300個數字,但并不是連續打印的,這是因為程序將這個打印任務進行了分解,分解后的任務會并行執行,所以不會按順序從0打印到299。

上面定義的任務是一個沒有返回值的打印任務,如果大任務是有返回值的任務,則可以讓任務繼承 RecursiveTask<T>,其中泛型參數 T 就代表了該任務的返回值類型。下面程序示范了使用 RecursiveTask 對一個長度為100的數組的元素值進行累加。

package com.jwen.demo4;import java.util.Random;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.Future;import java.util.concurrent.RecursiveTask;import java.util.function.Function;class CalTask extends RecursiveTask<Integer>{ // 每個“小任務”最多只累加20個數 private static final int THRESHOLD = 20; private int arr[]; private int start; private int end; // 累加從 start 到 end 的數組元素 public CalTask(int[] arr, int start, int end) { this.arr = arr; this.start = start; this.end = end; } @Override protected Integer compute() { int sum = 0; // 當 end 與 start 之間的差小于 THRESHOLD 時,開始進行實際累加 if(end-start<THRESHOLD) { for(int i=start;i<end;i++) { sum+=arr[i]; } return sum; }else { // 當 end 與 start 之間的差大于 THRESHOLD 時,即要累加的數超過20個時 // 將大任務分解成兩個“小任務” int middle = (start+end)/2; CalTask left = new CalTask(arr, start, middle); CalTask right = new CalTask(arr, middle, end); // 并行執行兩個“小任務” left.fork(); right.fork(); // 把兩個“小任務”累加的結果合并起來 return left.join()+right.join(); // ① } }}public class Sum { public static void main(String[] args) throws Exception{ int[] arr = new int[100]; Random rand = new Random(); int total = 0; // 初始化100個數字元素 for(int i=0,len = arr.length;i<len;i++) { int tmp = rand.nextInt(20); // 對數組元素賦值,并將數組元素的值添加到 sum 總和中 total +=(arr[i]=tmp); } System.out.println(total); // 創建一個通用池 ForkJoinPool pool = ForkJoinPool.commonPool(); // 提交可分解的 CalTask 任務 Future<Integer> future = pool.submit(new CalTask(arr, 0, arr.length)); System.out.println(future.get()); // 關閉線程池 pool.shutdown(); }}

上面程序與前一個程序基本相似,同樣是將任務進行了分解,并調用分解后的任務的 fork() 方法使它們并行執行。與前一個程序不同的是,現在任務是帶返回值的,因此程序還在①號代碼處將兩個分解后的“小任務”的返回值進行了合并。

運行上面程序,將可以看到程序通過 CalTask 計算出來的總和,與初始化數組元素時統計出來的總和總是相等,這表明程序一切正常。

Java 的確是一門非常優秀的編程語言,在多 CPU、多核 CPU 時代來到時,Java 語言的多線程已經為多核 CPU 做好了準備。

以上就是詳細分析JAVA 線程池的詳細內容,更多關于JAVA 線程池的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
主站蜘蛛池模板: 水冷式工业冷水机组_风冷式工业冷水机_水冷螺杆冷冻机组-深圳市普威机械设备有限公司 | 不锈钢法兰-碳钢法兰-法兰盘生产加工厂家-[鼎捷峰]-不锈钢法兰-碳钢法兰-法兰盘生产加工厂家-[鼎捷峰] | H型钢切割机,相贯线切割机,数控钻床,数控平面钻,钢结构设备,槽钢切割机,角钢切割机,翻转机,拼焊矫一体机 | 【星耀裂变】_企微SCRM_任务宝_视频号分销裂变_企业微信裂变增长_私域流量_裂变营销 | 纸塑分离机-纸塑分离清洗机设备-压力筛-碎浆机厂家金双联环保 | 国标白水泥,高标号白水泥,白水泥厂家-淄博华雪建材有限公司 | DDoS安全防护官网-领先的DDoS安全防护服务商 | 保定市泰宏机械制造厂-河北铸件厂-铸造厂-铸件加工-河北大件加工 | 钢化玻璃膜|手机钢化膜|钢化膜厂家|手机保护膜-【东莞市大象电子科技有限公司】 | 上海佳武自动化科技有限公司 | 多米诺-多米诺世界纪录团队-多米诺世界-多米诺团队培训-多米诺公关活动-多米诺创意广告-多米诺大型表演-多米诺专业赛事 | 小小作文网_中小学优秀作文范文大全| 耐酸泵,耐腐蚀真空泵,耐酸真空泵-淄博华舜耐腐蚀真空泵有限公司 精密模具-双色注塑模具加工-深圳铭洋宇通 | 包塑丝_高铁绑丝_地暖绑丝_涂塑丝_塑料皮铁丝_河北创筹金属丝网制品有限公司 | 真石漆,山东真石漆,真石漆厂家,真石漆价格-山东新佳涂料有限公司 | Honsberg流量计-Greisinger真空表-气压计-上海欧臻机电设备有限公司 | 大型工业风扇_工业大风扇_大吊扇_厂房车间降温-合昌大风扇 | 课件导航网_ppt课件_课件模板_课件下载_最新课件资源分享发布平台 | 书法培训-高考书法艺考培训班-山东艺霖书法培训凭实力挺进央美 | 即用型透析袋,透析袋夹子,药敏纸片,L型涂布棒-上海桥星贸易有限公司 | 智慧农业|农业物联网|现代农业物联网-托普云农物联网官方网站 | 奇酷教育-Python培训|UI培训|WEB大前端培训|Unity3D培训|HTML5培训|人工智能培训|JAVA开发的教育品牌 | 交流伺服电机|直流伺服|伺服驱动器|伺服电机-深圳市华科星电气有限公司 | 直读光谱仪,光谱分析仪,手持式光谱仪,碳硫分析仪,创想仪器官网 | 上海律师咨询_上海法律在线咨询免费_找对口律师上策法网-策法网 广东高华家具-公寓床|学生宿舍双层铁床厂家【质保十年】 | 通风气楼_通风天窗_屋顶风机-山东美创通风设备有限公司 | 小型UV打印机-UV平板打印机-大型uv打印机-UV打印机源头厂家 |松普集团 | 苗木价格-苗木批发-沭阳苗木基地-沭阳花木-长之鸿园林苗木场 | 湖州织里童装_女童男童中大童装_款式多尺码全_织里儿童网【官网】-嘉兴嘉乐网络科技有限公司 | 国产频谱分析仪-国产网络分析仪-上海坚融实业有限公司 | 杭州翻译公司_驾照翻译_专业人工翻译-杭州以琳翻译有限公司官网 组织研磨机-高通量组织研磨仪-实验室多样品组织研磨机-东方天净 | 国资灵活用工平台_全国灵活用工平台前十名-灵活用工结算小帮手 | 物流公司电话|附近物流公司电话上门取货 | 炭黑吸油计_测试仪,单颗粒子硬度仪_ASTM标准炭黑自销-上海贺纳斯仪器仪表有限公司(HITEC中国办事处) | 液晶拼接屏厂家_拼接屏品牌_拼接屏价格_监控大屏—北京维康 | 等离子表面处理机-等离子表面活化机-真空等离子清洗机-深圳市东信高科自动化设备有限公司 | 山西3A认证|太原AAA信用认证|投标AAA信用证书-山西AAA企业信用评级网 | elisa试剂盒价格-酶联免疫试剂盒-猪elisa试剂盒-上海恒远生物科技有限公司 | 网站建设,北京网站建设,北京网站建设公司,网站系统开发,北京网站制作公司,响应式网站,做网站公司,海淀做网站,朝阳做网站,昌平做网站,建站公司 | 防水套管-柔性防水套管-刚性防水套管-上海执品管件有限公司 | 酒吧霸屏软件_酒吧霸屏系统,酒吧微上墙,夜场霸屏软件,酒吧点歌软件,酒吧互动游戏,酒吧大屏幕软件系统下载 |