最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java中常見的6種線程池示例詳解

 更新時間:2020年11月04日 12:21:01   作者:AnonyStar  
這篇文章主要介紹了java中常見的6種線程池示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

  • 之前我們介紹了線程池的四種拒絕策略,了解了線程池參數(shù)的含義,那么今天我們來聊聊Java 中常見的幾種線程池,以及在jdk7 加入的 ForkJoin 新型線程池
  • 首先我們列出Java 中的六種線程池如下


線程池名稱 描述
FixedThreadPool 核心線程數(shù)與最大線程數(shù)相同
SingleThreadExecutor 一個線程的線程池
CachedThreadPool 核心線程為0,最大線程數(shù)為Integer. MAX_VALUE
ScheduledThreadPool 指定核心線程數(shù)的定時線程池
SingleThreadScheduledExecutor 單例的定時線程池
ForkJoinPool JDK 7 新加入的一種線程池

在了解集中線程池時我們先來熟悉一下主要幾個類的關系, ThreadPoolExecutor 的類圖,以及 Executors 的主要方法:

上面看到的類圖,方便幫助下面的理解和查看,我們可以看到一個核心類 ExecutorService , 這是我們線程池都實現(xiàn)的基類,我們接下來說的都是它的實現(xiàn)類。

FixedThreadPool

FixedThreadPool 線程池的特點是它的核心線程數(shù)和最大線程數(shù)一樣,我們可以看它的實現(xiàn)代碼在 Executors#newFixedThreadPool(int) 中,如下:

 public static ExecutorService newFixedThreadPool(int nThreads) {
 return new ThreadPoolExecutor(nThreads, nThreads,
  0L, TimeUnit.MILLISECONDS,
  new LinkedBlockingQueue<Runnable>());
 }

我們可以看到方法內創(chuàng)建線程調用的實際是 ThreadPoolExecutor 類,這是線程池的核心執(zhí)行器,傳入的 nThread 參數(shù)作為核心線程數(shù)和最大線程數(shù)傳入,隊列采用了一個鏈表結構的有界隊列。

  • 這種線程池我們可以看作是固定線程數(shù)的線程池,它只有在開始初始化的時候線程數(shù)會從0開始創(chuàng)建,但是創(chuàng)建好后就不再銷毀,而是全部作為常駐線程池,這里如果對線程池參數(shù)不理解的可以看之前文章 《解釋線程池各個參數(shù)的含義》。
  • 對于這種線程池他的第三個和第四個參數(shù)是沒意義,它們是空閑線程存活時間,這里都是常駐不存在銷毀,當線程處理不了時會加入到阻塞隊列,這是一個鏈表結構的有界阻塞隊列,最大長度是Integer. MAX_VALUE

SingleThreadExecutor

SingleThreadExecutor 線程的特點是它的核心線程數(shù)和最大線程數(shù)均為1,我們也可以將其任務是一個單例線程池,它的實現(xiàn)代碼是 Executors#newSingleThreadExcutor() , 如下:

 public static ExecutorService newSingleThreadExecutor() {
 return new FinalizableDelegatedExecutorService
 (new ThreadPoolExecutor(1, 1,
   0L, TimeUnit.MILLISECONDS,
   new LinkedBlockingQueue<Runnable>()));
 }

 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
 return new FinalizableDelegatedExecutorService
 (new ThreadPoolExecutor(1, 1,
   0L, TimeUnit.MILLISECONDS,
   new LinkedBlockingQueue<Runnable>(),
   threadFactory));
 }
  • 上述代碼中我們發(fā)現(xiàn)它有一個重載函數(shù),傳入了一個ThreadFactory 的參數(shù),一般在我們開發(fā)中會傳入我們自定義的線程創(chuàng)建工廠,如果不傳入則會調用默認的線程工廠
  • 我們可以看到它與 FixedThreadPool 線程池的區(qū)別僅僅是核心線程數(shù)和最大線程數(shù)改為了1,也就是說不管任務多少,它只會有唯一的一個線程去執(zhí)行
  • 如果在執(zhí)行過程中發(fā)生異常等導致線程銷毀,線程池也會重新創(chuàng)建一個線程來執(zhí)行后續(xù)的任務
  • 這種線程池非常適合所有任務都需要按被提交的順序來執(zhí)行的場景,是個單線程的串行。

CachedThreadPool

cachedThreadPool 線程池的特點是它的常駐核心線程數(shù)為0,正如其名字一樣,它所有的縣城都是臨時的創(chuàng)建,關于它的實現(xiàn)在 Executors#newCachedThreadPool() 中,代碼如下:

 public static ExecutorService newCachedThreadPool() {
 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
   60L, TimeUnit.SECONDS,
   new SynchronousQueue<Runnable>());
 }

 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
   60L, TimeUnit.SECONDS,
   new SynchronousQueue<Runnable>(),
   threadFactory);
 }
  • 從上述代碼中我們可以看到 CachedThreadPool 線程池中,最大線程數(shù)為 Integer.MAX_VALUE , 意味著他的線程數(shù)幾乎可以無限增加。
  • 因為創(chuàng)建的線程都是臨時線程,所以他們都會被銷毀,這里空閑 線程銷毀時間是60秒,也就是說當線程在60秒內沒有任務執(zhí)行則銷毀
  • 這里我們需要注意點,它使用了 SynchronousQueue 的一個阻塞隊列來存儲任務,這個隊列是無法存儲的,因為他的容量為0,它只負責對任務的傳遞和中轉,效率會更高,因為核心線程都為0,這個隊列如果存儲任務不存在意義。

ScheduledThreadPool

ScheduledThreadPool 線程池是支持定時或者周期性執(zhí)行任務,他的創(chuàng)建代碼 Executors.newSchedsuledThreadPool(int) 中,如下所示:

 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
 return new ScheduledThreadPoolExecutor(corePoolSize);
 }

 public static ScheduledExecutorService newScheduledThreadPool(
 int corePoolSize, ThreadFactory threadFactory) {
 return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
 }

我們發(fā)現(xiàn)這里調用了 ScheduledThreadPoolExecutor 這個類的構造函數(shù),進一步查看發(fā)現(xiàn) ScheduledThreadPoolExecutor 類是一個繼承了 ThreadPoolExecutor 的,同時實現(xiàn)了 ScheduledExecutorService 接口,我們看到它的幾個構造函數(shù)都是調用父類 ThreadPoolExecutor 的構造函數(shù)

 public ScheduledThreadPoolExecutor(int corePoolSize) {
 super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
 new DelayedWorkQueue());
 }

 public ScheduledThreadPoolExecutor(int corePoolSize,
   ThreadFactory threadFactory) {
 super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
 new DelayedWorkQueue(), threadFactory);
 }

 public ScheduledThreadPoolExecutor(int corePoolSize,
   RejectedExecutionHandler handler) {
 super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
 new DelayedWorkQueue(), handler);
 }

 public ScheduledThreadPoolExecutor(int corePoolSize,
   ThreadFactory threadFactory,
   RejectedExecutionHandler handler) {
 super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
 new DelayedWorkQueue(), threadFactory, handler);
 }

從上面代碼我們可以看到和其他線程池創(chuàng)建并沒有差異,只是這里的任務隊列是 DelayedWorkQueue 關于阻塞丟列我們下篇文章專門說,這里我們先創(chuàng)建一個周期性的線程池來看一下

 public static void main(String[] args) {
 ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
 // 1. 延遲一定時間執(zhí)行一次
 service.schedule(() ->{
 System.out.println("schedule ==> 云棲簡碼-i-code.online");
 },2, TimeUnit.SECONDS);

 // 2. 按照固定頻率周期執(zhí)行
 service.scheduleAtFixedRate(() ->{
 System.out.println("scheduleAtFixedRate ==> 云棲簡碼-i-code.online");
 },2,3,TimeUnit.SECONDS);

 //3. 按照固定頻率周期執(zhí)行
 service.scheduleWithFixedDelay(() -> {
 System.out.println("scheduleWithFixedDelay ==> 云棲簡碼-i-code.online");
 },2,5,TimeUnit.SECONDS);

 }

上面代碼是我們簡單創(chuàng)建了 newScheduledThreadPool ,同時演示了里面的三個核心方法,首先看執(zhí)行的結果:

首先我們看第一個方法 schedule , 它有三個參數(shù),第一個參數(shù)是線程任務,第二個 delay 表示任務執(zhí)行延遲時長,第三個 unit 表示延遲時間的單位,如上面代碼所示就是延遲兩秒后執(zhí)行任務

 public ScheduledFuture<?> schedule(Runnable command,
   long delay, TimeUnit unit);

第二個方法是 scheduleAtFixedRate 如下, 它有四個參數(shù), command 參數(shù)表示執(zhí)行的線程任務 , initialDelay 參數(shù)表示第一次執(zhí)行的延遲時間, period 參數(shù)表示第一次執(zhí)行之后按照多久一次的頻率來執(zhí)行,最后一個參數(shù)是時間單位。如上面案例代碼所示,表示兩秒后執(zhí)行第一次,之后按每隔三秒執(zhí)行一次

 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
    long initialDelay,
    long period,
    TimeUnit unit);

第三個方法是 scheduleWithFixedDelay 如下,它與上面方法是非常類似的,也是周期性定時執(zhí)行, 參數(shù)含義和上面方法一致。這個方法和 scheduleAtFixedRate 的區(qū)別主要在于時間的起點計時不同

 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
    long initialDelay,
    long delay,
    TimeUnit unit);

scheduleAtFixedRate 是以任務開始的時間為時間起點來計時,時間到就執(zhí)行第二次任務,與任務執(zhí)行所花費的時間無關;而 scheduleWithFixedDelay 是以任務執(zhí)行結束的時間點作為計時的開始。如下所示

SingleThreadScheduledExecutor 它實際和 ScheduledThreadPool 線程池非常相似,它只是 ScheduledThreadPool 的一個特例,內部只有一個線程,它只是將 ScheduledThreadPool 的核心線程數(shù)設置為了 1。如源碼所示:

 public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
 return new DelegatedScheduledExecutorService
 (new ScheduledThreadPoolExecutor(1));
 }

上面我們介紹了五種常見的線程池,對于這些線程池我們可以從核心線程數(shù)、最大線程數(shù)、存活時間三個維度進行一個簡單的對比,有利于我們加深對這幾種線程池的記憶。 

FixedThreadPool SingleThreadExecutor CachedThreadPool ScheduledThreadPool SingleThreadScheduledExecutor
corePoolSize 構造函數(shù)傳入 1 0 構造函數(shù)傳入 1
maxPoolSize 同corePoolSize 1 Integer. MAX_VALUE Integer. MAX_VALUE Integer. MAX_VALUE
keepAliveTime 0 0 60 0 0

ForkJoinPool ForkJoinPool 這是一個在 JDK7 引入的新新線程池,它的主要特點是可以充分利用多核 CPU , 可以把一個任務拆分為多個子任務,這些子任務放在不同的處理器上并行執(zhí)行,當這些子任務執(zhí)行結束后再把這些結果合并起來,這是一種分治思想。 ForkJoinPool 也正如它的名字一樣,第一步進行 Fork 拆分,第二步進行 Join 合并,我們先來看一下它的類圖結構

ForkJoinPool 的使用也是通過調用 submit(ForkJoinTask<T> task) 或 invoke(ForkJoinTask<T> task) 方法來執(zhí)行指定任務了。其中任務的類型是 ForkJoinTask 類,它代表的是一個可以合并的子任務,他本身是一個抽象類,同時還有兩個常用的抽象子類 RecursiveAction 和 RecursiveTask ,其中 RecursiveTask 表示的是有返回值類型的任務,而 RecursiveAction 則表示無返回值的任務。下面是它們的類圖:

下面我們通過一個簡單的代碼先來看一下如何使用 ForkJoinPool 線程池

/**
 * @url: i-code.online
 * @author: AnonyStar
 * @time: 2020/11/2 10:01
 */
public class ForkJoinApp1 {

 /**
 目標: 打印0-200以內的數(shù)字,進行分段每個間隔為10以上,測試forkjoin
 */
 public static void main(String[] args) {
 // 創(chuàng)建線程池,
 ForkJoinPool joinPool = new ForkJoinPool();
 // 創(chuàng)建根任務
 SubTask subTask = new SubTask(0,200);
 // 提交任務
 joinPool.submit(subTask);
 //讓線程阻塞等待所有任務完成 在進行關閉
 try {
 joinPool.awaitTermination(2, TimeUnit.SECONDS);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 joinPool.shutdown();
 }
}

class SubTask extends RecursiveAction {

 int startNum;
 int endNum;

 public SubTask(int startNum,int endNum){
 super();
 this.startNum = startNum;
 this.endNum = endNum;
 }

 @Override
 protected void compute() {

 if (endNum - startNum < 10){
 // 如果分裂的兩者差值小于10 則不再繼續(xù),直接打印
 System.out.println(Thread.currentThread().getName()+": [startNum:"+startNum+",endNum:"+endNum+"]");
 }else {
 // 取中間值
 int middle = (startNum + endNum) / 2;
 //創(chuàng)建兩個子任務,以遞歸思想,
 SubTask subTask = new SubTask(startNum,middle);
 SubTask subTask1 = new SubTask(middle,endNum);
 //執(zhí)行任務, fork() 表示異步的開始執(zhí)行
 subTask.fork();
 subTask1.fork();
 }
 }
}

結果:

從上面的案例我們可以看到我們,創(chuàng)建了很多個線程執(zhí)行,因為我測試的電腦是12線程的,所以這里實際是創(chuàng)建了12個線程,也側面說明了充分調用了每個處理的線程處理能力 上面案例其實我們發(fā)現(xiàn)很熟悉的味道,那就是以前接觸過的遞歸思想,將上面的案例圖像化如下,更直觀的看到,

上面的例子是無返回值的案例,下面我們來看一個典型的有返回值的案例,相信大家都聽過及很熟悉斐波那契數(shù)列,這個數(shù)列有個特點就是最后一項的結果等于前兩項的和,如: 0,1,1,2,3,5...f(n-2)+f(n-1) , 即第0項為0 ,第一項為1,則第二項為 0+1=1 ,以此類推。我們最初的解決方法就是使用遞歸來解決,如下計算第n項的數(shù)值:

 private int num(int num){
 if (num <= 1){
 return num;
 }
 num = num(num-1) + num(num -2);
 return num;
 }

從上面簡單代碼中可以看到,當 n<=1 時返回 n , 如果 n>1 則計算前一項的值 f1 ,在計算前兩項的值 f2 , 再將兩者相加得到結果,這就是典型的遞歸問題,也是對應我們的 ForkJoin 的工作模式,如下所示,根節(jié)點產(chǎn)生子任務,子任務再次衍生出子子任務,到最后在進行整合匯聚,得到結果。

我們通過 ForkJoinPool 來實現(xiàn)斐波那契數(shù)列的計算,如下展示:

/**
 * @url: i-code.online
 * @author: AnonyStar
 * @time: 2020/11/2 10:01
 */
public class ForkJoinApp3 {

 public static void main(String[] args) throws ExecutionException, InterruptedException {
 ForkJoinPool pool = new ForkJoinPool();
 //計算第二是項的數(shù)值
 final ForkJoinTask<Integer> submit = pool.submit(new Fibonacci(20));
 // 獲取結果,這里獲取的就是異步任務的最終結果
 System.out.println(submit.get());

 }
}

class Fibonacci extends RecursiveTask<Integer>{

 int num;
 public Fibonacci(int num){
 this.num = num;
 }

 @Override
 protected Integer compute() {
 if (num <= 1) return num;
 //創(chuàng)建子任務
 Fibonacci subTask1 = new Fibonacci(num - 1);
 Fibonacci subTask2 = new Fibonacci(num - 2);
 // 執(zhí)行子任務
 subTask1.fork();
 subTask2.fork();
 //獲取前兩項的結果來計算和
 return subTask1.join()+subTask2.join();
 }
}

通過 ForkJoinPool 可以極大的發(fā)揮多核處理器的優(yōu)勢,尤其非常適合用于遞歸的場景,例如樹的遍歷、最優(yōu)路徑搜索等場景。 上面說的是 ForkJoinPool 的使用上的,下面我們來說一下其內部的構造,對于我們前面說的幾種線程池來說,它們都是里面只有一個隊列,所有的線程共享一個。但是在 ForkJoinPool 中,其內部有一個共享的任務隊列,除此之外每個線程都有一個對應的雙端隊列 Deque , 當一個線程中任務被 Fork 分裂了,那么分裂出來的子任務就會放入到對應的線程自己的 Deque 中,而不是放入公共隊列。這樣對于每個線程來說成本會降低很多,可以直接從自己線程的隊列中獲取任務而不需要去公共隊列中爭奪,有效的減少了線程間的資源競爭和切換。

有一種情況,當線程有多個如 t1,t2,t3... ,在某一段時間線程 t1 的任務特別繁重,分裂了數(shù)十個子任務,但是線程 t0 此時卻無事可做,它自己的 deque 隊列為空,這時為了提高效率, t0 就會想辦法幫助 t1 執(zhí)行任務,這就是“ work-stealing ”的含義。 雙端隊列 deque 中,線程 t1 獲取任務的邏輯是后進先出,也就是 LIFO(Last In Frist Out) ,而線程 t0 在“ steal ”偷線程 t1 的 deque 中的任務的邏輯是先進先出,也就是 FIFO(Fast In Frist Out) ,如圖所示,圖中很好的描述了兩個線程使用雙端隊列分別獲取任務的情景。你可以看到,使用 “ work-stealing ” 算法和雙端隊列很好地平衡了各線程的負載。

總結

到此這篇關于java中常見的6種線程池示例詳解的文章就介紹到這了,更多相關java常見線程池內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • MyBatis類型處理器TypeHandler的作用及說明

    MyBatis類型處理器TypeHandler的作用及說明

    這篇文章主要介紹了MyBatis類型處理器TypeHandler的作用及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • MyBatis-Plus 動態(tài)表名SQL解析器的實現(xiàn)

    MyBatis-Plus 動態(tài)表名SQL解析器的實現(xiàn)

    這篇文章主要介紹了MyBatis-Plus 動態(tài)表名SQL解析器的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • MyBatis-Plus模糊查詢特殊字符串轉義的實現(xiàn)

    MyBatis-Plus模糊查詢特殊字符串轉義的實現(xiàn)

    使用MyBatis中的模糊查詢時,當查詢關鍵字中包括有_、\、%時,查詢關鍵字失效,本文主要介紹了MyBatis-Plus模糊查詢特殊字符串轉義的實現(xiàn),感興趣的可以了解一下
    2024-06-06
  • Springboot跨域處理的多種方式小結

    Springboot跨域處理的多種方式小結

    當一臺服務器資源從另一臺服務器(不同 的域名或者端口)請求一個資源或者接口,就會發(fā)起一個跨域 HTTP 請求,這篇文章主要介紹了Springboot跨域處理的多種方式小結,需要的朋友可以參考下
    2023-11-11
  • SpringBoot整合Shiro的代碼詳解

    SpringBoot整合Shiro的代碼詳解

    shiro是一個權限框架,它提供了很方便的權限認證和登錄的功能.下面通過本文給大家分享SpringBoot整合Shiro的代碼詳解,需要的的朋友參考下吧
    2017-08-08
  • GsonFormat快速生成JSon實體類的實現(xiàn)

    GsonFormat快速生成JSon實體類的實現(xiàn)

    GsonFormat主要用于使用Gson庫將JSONObject格式的String?解析成實體,本文主要介紹了GsonFormat快速生成JSon實體類的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-05-05
  • SpringBoot(十)之郵件服務

    SpringBoot(十)之郵件服務

    這篇文章給大家介紹了SpringBoot郵件服務的相關知識,此文是使用springboot最新版本1.5.3進行開發(fā)的。本文給大家介紹的非常詳細,具有參考借鑒價值,需要的的朋友參考下吧
    2017-05-05
  • Java中堆和棧的區(qū)別詳解

    Java中堆和棧的區(qū)別詳解

    這篇文章主要介紹了Java中堆和棧的區(qū)別詳解,所有的Java程序都運行在JVM虛擬機內部,我們這里介紹的自然是JVM(虛擬)內存中的堆和棧,需要的朋友可以參考下
    2015-01-01
  • java得到某年某周的第一天實現(xiàn)思路及代碼

    java得到某年某周的第一天實現(xiàn)思路及代碼

    某年某周的第一天,此功能如何使用java編程得到呢?既然有了問題就有解決方法,感興趣的朋友可以了解下本文,或許會給你帶來意想不到的收獲哦
    2013-01-01
  • Java 線程池_動力節(jié)點Java學院整理

    Java 線程池_動力節(jié)點Java學院整理

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

最新評論

盖州市| 宜春市| 如东县| 会同县| 略阳县| 大埔县| 莆田市| 凌海市| 和田市| 桂阳县| 新巴尔虎左旗| 巴林右旗| 阳新县| 沽源县| 咸丰县| 哈巴河县| 新乐市| 乡城县| 铁力市| 平凉市| 浠水县| 拜城县| 寿宁县| 丰顺县| 延长县| 怀宁县| 云龙县| 盐山县| 蛟河市| 湘阴县| 南木林县| 甘南县| 奉化市| 平原县| 江门市| 玉溪市| 拉萨市| 冕宁县| 甘谷县| 保靖县| 邹平县|