大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國(guó)咨詢/投訴熱線:400-8080-105
動(dòng)力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁(yè) 學(xué)習(xí)攻略 Java學(xué)習(xí) 深入學(xué)習(xí)Java線程池:Java線程池學(xué)習(xí)教程

深入學(xué)習(xí)Java線程池:Java線程池學(xué)習(xí)教程

更新時(shí)間:2019-09-24 09:39:35 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽2260次



image.png

  線程池是多線程編程中的核心概念,簡(jiǎn)單來(lái)說(shuō)就是一組可以執(zhí)行任務(wù)的空閑線程。


  首先,我們了解一下多線程框架模型,明白為什么需要線程池。


  線程是在一個(gè)進(jìn)程中可以執(zhí)行一系列指令的執(zhí)行環(huán)境,或稱運(yùn)行程序。多線程編程指的是用多個(gè)線程并行執(zhí)行多個(gè)任務(wù)。當(dāng)然,JVM對(duì)多線程有良好的支持。


  盡管這帶來(lái)了諸多優(yōu)勢(shì),首當(dāng)其沖的就是程序性能提高,但多線程編程也有缺點(diǎn)——增加了代碼復(fù)雜度、同步問(wèn)題、非預(yù)期結(jié)果和增加創(chuàng)建線程的開(kāi)銷。


  在這篇文章中,我們來(lái)了解一下如何使用Java線程池來(lái)緩解這些問(wèn)題。


  為什么使用線程池?


  創(chuàng)建并開(kāi)啟一個(gè)線程開(kāi)銷很大。如果我們每次需要執(zhí)行任務(wù)時(shí)重復(fù)這個(gè)步驟,那將會(huì)是一筆巨大的性能開(kāi)銷,這也是我們希望通過(guò)多線程解決的問(wèn)題。


  為了更好理解創(chuàng)建和開(kāi)啟一個(gè)線程的開(kāi)銷,讓我們來(lái)看一看JVM在后臺(tái)做了哪些事:


  為線程棧分配內(nèi)存,保存每個(gè)線程方法調(diào)用的棧幀。


  每個(gè)棧幀包括本地變量數(shù)組、返回值、操作棧和常量池


  一些JVM支持本地方法,也將分配本地方法棧


  每個(gè)線程獲得一個(gè)程序計(jì)數(shù)器,標(biāo)識(shí)處理器正在執(zhí)行哪條指令


  系統(tǒng)創(chuàng)建本地線程,與Java線程對(duì)應(yīng)


  和線程相關(guān)的描述符被添加到JVM內(nèi)部數(shù)據(jù)結(jié)構(gòu)


  線程共享堆和方法區(qū)


  當(dāng)然,這些步驟的具體細(xì)節(jié)取決于JVM和操作系統(tǒng)。


  另外,更多的線程意味著更多工作量,系統(tǒng)需要調(diào)度和決定哪個(gè)線程接下來(lái)可以訪問(wèn)資源。


  線程池通過(guò)減少需要的線程數(shù)量并管理線程生命周期,來(lái)幫助我們緩解性能問(wèn)題。


  本質(zhì)上,線程在我們使用前一直保存在線程池中,在執(zhí)行完任務(wù)之后,線程會(huì)返回線程池等待下次使用。這種機(jī)制在執(zhí)行很多小任務(wù)的系統(tǒng)中十分有用。


  Java線程池


  Java通過(guò)executor對(duì)象來(lái)實(shí)現(xiàn)自己的線程池模型。可以使用executor接口或其他線程池的實(shí)現(xiàn),它們都允許細(xì)粒度的控制。


  java.util.concurrent包中有以下接口:


  Executor——執(zhí)行任務(wù)的簡(jiǎn)單接口


  ExecutorService——一個(gè)較復(fù)雜的接口,包含額外方法來(lái)管理任務(wù)和executor本身


  ScheduledExecutorService——擴(kuò)展自ExecutorService,增加了執(zhí)行任務(wù)的調(diào)度方法


  除了這些接口,這個(gè)包中也提供了Executors類直接獲取實(shí)現(xiàn)了這些接口的executor實(shí)例


  一般來(lái)說(shuō),一個(gè)Java線程池包含以下部分:


  工作線程的池子,負(fù)責(zé)管理線程


  線程工廠,負(fù)責(zé)創(chuàng)建新線程


  等待執(zhí)行的任務(wù)隊(duì)列


  在下面的章節(jié),讓我們仔細(xì)看一看Java類和接口如何為線程池提供支持。


  Executors類和Executor接口


  Executors類包含工廠方法創(chuàng)建不同類型的線程池,Executor是個(gè)簡(jiǎn)單的線程池接口,只有一個(gè)execute()方法。


  我們通過(guò)一個(gè)例子來(lái)結(jié)合使用這兩個(gè)類(接口),首先創(chuàng)建一個(gè)單線程的線程池,然后用它執(zhí)行一個(gè)簡(jiǎn)單的語(yǔ)句:


  Executorexecutor=Executors.newSingleThreadExecutor();

  executor.execute(()->System.out.println("Singlethreadpooltest"));


  注意語(yǔ)句寫(xiě)成了lambda表達(dá)式,會(huì)被自動(dòng)推斷成Runnable類型。


  如果有工作線程可用,execute()方法將執(zhí)行語(yǔ)句,否則就把Runnable任務(wù)放進(jìn)隊(duì)列,等待線程可用。


  基本上,executor代替了顯式創(chuàng)建和管理線程。


  Executors類里的工廠方法可以創(chuàng)建很多類型的線程池:


  newSingleThreadExecutor():包含單個(gè)線程和無(wú)界隊(duì)列的線程池,同一時(shí)間只能執(zhí)行一個(gè)任務(wù)


  newFixedThreadPool():包含固定數(shù)量線程并共享無(wú)界隊(duì)列的線程池;當(dāng)所有線程處于工作狀態(tài),有新任務(wù)提交時(shí),任務(wù)在隊(duì)列中等待,直到一個(gè)線程變?yōu)榭捎脿顟B(tài)


  newCachedThreadPool():只有需要時(shí)創(chuàng)建新線程的線程池


  newWorkStealingThreadPool():基于工作竊取(work-stealing)算法的線程池,后面章節(jié)詳細(xì)說(shuō)明


  接下來(lái),讓我們看一下ExecutorService接口提供了哪些新功能


  ExecutorService


  創(chuàng)建ExecutorService方式之一便是通過(guò)Excutors類的工廠方法。


  ExecutorServiceexecutor=Executors.newFixedThreadPool(10);


  Besidestheexecute()method,thisinterfacealsodefinesasimilarsubmit()methodthatcanreturnaFutureobject:


  除了execute()方法,接口也定義了相似的submit()方法,這個(gè)方法可以返回一個(gè)Future對(duì)象。


  Callable<Double>callableTask=()->{

  returnemployeeService.calculateBonus(employee);

  };

  Future<Double>future=executor.submit(callableTask);

  //executeotheroperations

  try{

  if(future.isDone()){

  doubleresult=future.get();

  }

  }catch(InterruptedException|ExecutionExceptione){

  e.printStackTrace();

  }


  從上面的例子可以看到,F(xiàn)uture接口可以返回Callable類型任務(wù)的結(jié)果,而且能顯示任務(wù)的執(zhí)行狀態(tài)。


  當(dāng)沒(méi)有任務(wù)等待執(zhí)行時(shí),ExecutorService并不會(huì)自動(dòng)銷毀,所以你可以使用shutdown()或shutdownNow()來(lái)顯式關(guān)閉它。


  executor.shutdown();


  ScheduledExecutorService


  這是ExecutorService的一個(gè)子接口,增加了調(diào)度任務(wù)的方法。


  ScheduledExecutorServiceexecutor=Executors.newScheduledThreadPool(10);

  schedule()方法的參數(shù)指定執(zhí)行的方法、延時(shí)和TimeUnit


  Future<Double>future=executor.schedule(callableTask,2,TimeUnit.MILLISECONDS);


  另外,這個(gè)接口定義了其他兩個(gè)方法:


  executor.scheduleAtFixedRate(

  ()->System.out.println("FixedRateScheduled"),2,2000,TimeUnit.MILLISECONDS);

  executor.scheduleWithFixedDelay(

  ()->System.out.println("FixedDelayScheduled"),2,2000,TimeUnit.MILLISECONDS);


  scheduleAtFixedRate()方法延時(shí)2毫秒執(zhí)行任務(wù),然后每2秒重復(fù)一次。相似的,scheduleWithFixedDelay()方法延時(shí)2毫秒后執(zhí)行第一次,然后在上一次執(zhí)行完成2秒后再次重復(fù)執(zhí)行。


  在下面的章節(jié),我們來(lái)看一下ExecutorService接口的兩個(gè)實(shí)現(xiàn):ThreadPoolExecutor和ForkJoinPool。


  ThreadPoolExecutor


  這個(gè)線程池的實(shí)現(xiàn)增加了配置參數(shù)的能力。創(chuàng)建ThreadPoolExecutor對(duì)象最方便的方式就是通過(guò)Executors工廠方法:


  ThreadPoolExecutorexecutor=(ThreadPoolExecutor)Executors.newFixedThreadPool(10);


  這種情況下,線程池按照默認(rèn)值預(yù)配置了參數(shù)。線程數(shù)量由以下參數(shù)控制:


  corePoolSize和maximumPoolSize:表示線程數(shù)量的范圍


  keepAliveTime:決定了額外線程存活時(shí)間


  我們深入了解一下這些參數(shù)如何使用。


  當(dāng)一個(gè)任務(wù)被提交時(shí),如果執(zhí)行中的線程數(shù)量小于corePoolSize,一個(gè)新的線程被創(chuàng)建。如果運(yùn)行的線程數(shù)量大于corePoolSize,但小于maximumPoolSize,并且任務(wù)隊(duì)列已滿時(shí),依然會(huì)創(chuàng)建新的線程。如果多于corePoolSize的線程空閑時(shí)間超過(guò)keepAliveTime,它們會(huì)被終止。


  上面那個(gè)例子中,newFixedThreadPool()方法創(chuàng)建的線程池,corePoolSize=maximumPoolSize=10并且keepAliveTime為0秒。


  如果你使用newCachedThreadPool()方法,創(chuàng)建的線程池maximumPoolSize為Integer.MAX_VALUE,并且keepAliveTime為60秒。


  ThreadPoolExecutorcachedPoolExecutor

  =(ThreadPoolExecutor)Executors.newCachedThreadPool();

  Theparameterscanalsobesetthroughaconstructororthroughsettermethods:


  這些參數(shù)也可以通過(guò)構(gòu)造函數(shù)或setter方法設(shè)置:

  ThreadPoolExecutorexecutor=newThreadPoolExecutor(

  4,6,60,TimeUnit.SECONDS,newLinkedBlockingQueue<Runnable>()

  );

  executor.setMaximumPoolSize(8);


  ThreadPoolExecutor的一個(gè)子類便是ScheduledThreadPoolExecutor,它實(shí)現(xiàn)了ScheduledExecutorService接口。你可以通過(guò)newScheduledThreadPool()工廠方法來(lái)創(chuàng)建這種類型的線程池。


  ScheduledThreadPoolExecutorexecutor


  =(ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(5);


  上面語(yǔ)句創(chuàng)建了一個(gè)線程池,corePoolSize為5,maximumPoolSize無(wú)限制,keepAliveTime為0秒。


  ForkJoinPool


  另一個(gè)線程池的實(shí)現(xiàn)是ForkJoinPool類。它實(shí)現(xiàn)了ExecutorService接口,并且是Java7中fork/join框架的重要組件。


  fork/join框架基于“工作竊取算法”。簡(jiǎn)而言之,意思就是執(zhí)行完任務(wù)的線程可以從其他運(yùn)行中的線程“竊取”工作。


  ForkJoinPool適用于任務(wù)創(chuàng)建子任務(wù)的情況,或者外部客戶端創(chuàng)建大量小任務(wù)到線程池。


  這種線程池的工作流程如下:


  創(chuàng)建ForkJoinTask子類


  根據(jù)某種條件將任務(wù)切分成子任務(wù)


  調(diào)用執(zhí)行任務(wù)


  將任務(wù)結(jié)果合并


  實(shí)例化對(duì)象并添加到池中


  創(chuàng)建一個(gè)ForkJoinTask,你可以選擇RecursiveAction或RecursiveTask這兩個(gè)子類,后者有返回值。


  我們來(lái)實(shí)現(xiàn)一個(gè)繼承RecursiveTask的類,計(jì)算階乘,并把任務(wù)根據(jù)閾值劃分成子任務(wù)。

      image.png

  這個(gè)類需要實(shí)現(xiàn)的主要方法就是重寫(xiě)compute()方法,用于合并每個(gè)子任務(wù)的結(jié)果。


  具體劃分任務(wù)邏輯在createSubtasks()方法中:

      image.png

  最后,calculate()方法包含一定范圍內(nèi)的乘數(shù)。

     image.png

  接下來(lái),任務(wù)可以添加到線程池:


  ForkJoinPoolpool=ForkJoinPool.commonPool();

  BigIntegerresult=pool.invoke(newFactorialTask(100));


  ThreadPoolExecutor與ForkJoinPool對(duì)比


  初看上去,似乎fork/join框架帶來(lái)性能提升。但是這取決于你所解決問(wèn)題的類型。


  當(dāng)選擇線程池時(shí),非常重要的一點(diǎn)是牢記創(chuàng)建、管理線程以及線程間切換執(zhí)行會(huì)帶來(lái)的開(kāi)銷。


  ThreadPoolExecutor可以控制線程數(shù)量和每個(gè)線程執(zhí)行的任務(wù)。這很適合你需要在不同的線程上執(zhí)行少量巨大的任務(wù)。


  相比較而言,F(xiàn)orkJoinPool基于線程從其他線程“竊取”任務(wù)。正因如此,當(dāng)任務(wù)可以分割成小任務(wù)時(shí)可以提高效率。


  為了實(shí)現(xiàn)工作竊取算法,fork/join框架使用兩種隊(duì)列:


  包含所有任務(wù)的主要隊(duì)列


  每個(gè)線程的任務(wù)隊(duì)列


  當(dāng)線程執(zhí)行完自己任務(wù)隊(duì)列中的任務(wù),它們?cè)噲D從其他隊(duì)列獲取任務(wù)。為了使這一過(guò)程更加高效,線程任務(wù)隊(duì)列使用雙端隊(duì)列(doubleendedqueue)數(shù)據(jù)結(jié)構(gòu),一端與線程交互,另一端用于“竊取”任務(wù)。


  來(lái)自TheHDeveloper的圖很好的表現(xiàn)出了這一過(guò)程:

image.png

  和這種模型相比,ThreadPoolExecutor只使用一個(gè)主要隊(duì)列。


  最后要注意的一點(diǎn)ForkJoinPool只適用于任務(wù)可以創(chuàng)建子任務(wù)。否則它和ThreadPoolExecutor沒(méi)區(qū)別,甚至開(kāi)銷更大。


  跟蹤線程池的執(zhí)行


  現(xiàn)在我們對(duì)Java線程池生態(tài)系統(tǒng)有了基本的了解,讓我們通過(guò)一個(gè)使用了線程池的應(yīng)用,來(lái)看一看執(zhí)行中到底發(fā)生了什么。


  通過(guò)在FactorialTask的構(gòu)造函數(shù)和calculate()方法中加入日志語(yǔ)句,你可以看到下面調(diào)用序列:

      image.png

  你可以看到創(chuàng)建了很多任務(wù),但只有3個(gè)工作線程——所以任務(wù)通過(guò)線程池被可用線程處理。


  也可以看到在放到執(zhí)行池之前,主線程中對(duì)象如何被創(chuàng)建。


  使用Prefix這一類可視化的日志工具是一個(gè)很棒的方式來(lái)探索和理解運(yùn)行時(shí)的線程池。


  記錄線程池日志的核心便是保證在日志信息中方便辨識(shí)線程名字。Log4J2通過(guò)使用布局能夠很好完成這種工作。


  使用線程池的潛在風(fēng)險(xiǎn)


  盡管線程池有巨大優(yōu)勢(shì),你在使用中仍會(huì)遇到一些問(wèn)題,比如:


  用的線程池過(guò)大或過(guò)小:如果線程池包含太多線程,會(huì)明顯的影響應(yīng)用的性能;另一方面,線程池太小并不能帶來(lái)所期待的性能提升。


  正如其他多線程情形一樣,死鎖也會(huì)發(fā)生。舉個(gè)例子,一個(gè)任務(wù)可能等待另一個(gè)任務(wù)完成,而后者并沒(méi)有可用線程處理執(zhí)行。所以說(shuō)避免任務(wù)之間的依賴是個(gè)好習(xí)慣。


  等待執(zhí)行時(shí)間很長(zhǎng)的任務(wù):為了避免長(zhǎng)時(shí)間阻塞線程,你可以指定最大等待時(shí)間,并決定過(guò)期任務(wù)是拒絕處理還是重新加入隊(duì)列。


  為了降低風(fēng)險(xiǎn),你必須根據(jù)要處理的任務(wù),來(lái)謹(jǐn)慎選擇線程池的類型和參數(shù)。對(duì)你的系統(tǒng)進(jìn)行壓力測(cè)試也是值得的,它可以幫你獲取真實(shí)環(huán)境下的系統(tǒng)行為數(shù)據(jù)。


  結(jié)論


  線程池有很大優(yōu)勢(shì),簡(jiǎn)單來(lái)說(shuō)就是可以將任務(wù)的執(zhí)行從線程的創(chuàng)建和管理中分離。另外,如果使用得當(dāng),它們可以極大提高應(yīng)用的性能。


  如果你學(xué)會(huì)充分利用線程池,Java生態(tài)系統(tǒng)好處便是其中有很多成熟穩(wěn)定的線程池實(shí)現(xiàn)。


  以上就是動(dòng)力節(jié)點(diǎn)java培訓(xùn)機(jī)構(gòu)小編介紹的“深入學(xué)習(xí)Java線程池:Java線程池學(xué)習(xí)教程”的內(nèi)容,希望對(duì)大家有幫助,更多java最新資訊請(qǐng)繼續(xù)關(guān)注動(dòng)力節(jié)點(diǎn)java培訓(xùn)機(jī)構(gòu)官網(wǎng),每天會(huì)有精彩內(nèi)容分享與你。

提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)

  • 全國(guó)校區(qū) 2025-10-20 搶座中
免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 91福利影院 | 亚洲综合视频网 | 欧洲午夜视频 | 久久久久国产一级毛片高清版 | 色婷婷91| 福利在线免费 | 综合激情在线 | 日本不卡一区二区三区视频 | 国内一区二区 | 欧美国产成人免费观看永久视频 | 337p欧美超大胆日本人术艺术 | 9热在线精品视频观看 | 素人巨乳被调教 | 国产在线精品福利91香蕉 | 日本特级视频 | 天天玩天天操 | 四虎国产精品免费入口 | 九九久久精品这里久久网 | 日韩美女中文字幕 | 涩涩免费播放观看在线视频 | 日本黄色网址视频 | 国产精品每日更新在线观看 | 四虎永久成人免费 | 麻豆a| 一级女性全黄生活片免费看 | 九九色综合网 | 国产成人一区 | 国产福利第一视频 | 香蕉97超级碰碰碰碰碰久 | 国产亚洲欧洲国产综合一区 | 奇米影视奇米色777欧美 | 天天做日日做 | 精品视频 久久久 | 香蕉网站狼人久久五月亭亭 | 久草在线视频免费 | 91精品成人免费国产片 | 91精品国产色综合久久不 | 天天怕夜夜怕狠狠怕 | 综合在线视频精品专区 | 国产目拍亚洲精品一区二区三区 | 手机看片日韩日韩国产在线看 |