Java線程池的四種用法與使用場景

2020-12-12 51CTO

 一、如下方式存在的問題

  1. new Thread() { 
  2.     @Override 
  3.     public void run() { 
  4.         // 業務邏輯 
  5.     } 
  6. }.start(); 

1、首先頻繁的創建、銷毀對象是一個很消耗性能的事情;2、如果用戶量比較大,導致佔用過多的資源,可能會導致我們的服務由於資源不足而宕機;3、綜上所述,在實際的開發中,這種操作其實是不可取的一種方式。

二、使用線程池有什麼優點

1、線程池中線程的使用率提升,減少對象的創建、銷毀;2、線程池可以控制線程數,有效的提升伺服器的使用資源,避免由於資源不足而發生宕機等問題;

三、線程池的四種使用方式

1、newCachedThreadPool

創建一個線程池,如果線程池中的線程數量過大,它可以有效的回收多餘的線程,如果線程數不足,那麼它可以創建新的線程。

  1. public static void method() throws Exception { 
  2.  
  3.     ExecutorService executor = Executors.newCachedThreadPool(); 
  4.  
  5.     for (int i = 0; i < 5; i++) { 
  6.  
  7.         final int index = i; 
  8.  
  9.         Thread.sleep(1000); 
  10.  
  11.         executor.execute(new Runnable() { 
  12.             @Override 
  13.             public void run() { 
  14.                 System.out.println(Thread.currentThread().getName() + "  " + index); 
  15.             } 
  16.         }); 
  17.     } 

執行結果

通過分析我看可以看到,至始至終都由一個線程執行,實現了線程的復用,並沒有創建多餘的線程。如果當我們的業務需要一定的時間進行處理,那麼將會出現什麼結果。我們來模擬一下。

可以明顯的看出,現在就需要幾條線程來交替執行。

不足:這種方式雖然可以根據業務場景自動的擴展線程數來處理我們的業務,但是最多需要多少個線程同時處理缺是我們無法控制的;

優點:如果當第二個任務開始,第一個任務已經執行結束,那麼第二個任務會復用第一個任務創建的線程,並不會重新創建新的線程,提高了線程的復用率;

2、newFixedThreadPool

這種方式可以指定線程池中的線程數。舉個慄子,如果一間澡堂子最大只能容納20個人同時洗澡,那麼後面來的人只能在外面排隊等待。如果硬往裡衝,那麼只會出現一種情景,摩擦摩擦...

首先測試一下最大容量為一個線程,那麼會不會是我們預測的結果。

  1. public static void method_01() throws InterruptedException { 
  2.  
  3.     ExecutorService executor = Executors.newFixedThreadPool(1); 
  4.  
  5.     for (int i = 0; i < 10; i++) { 
  6.  
  7.         Thread.sleep(1000); 
  8.         final int index = i; 
  9.  
  10.         executor.execute(() -> { 
  11.             try { 
  12.                 Thread.sleep(2 * 1000); 
  13.             } catch (InterruptedException e) { 
  14.                 e.printStackTrace(); 
  15.             } 
  16.             System.out.println(Thread.currentThread().getName() + "  " + index); 
  17.         }); 
  18.     } 
  19.     executor.shutdown(); 

執行結果

我們改為3條線程再來看下結果

優點:兩個結果綜合說明,newFixedThreadPool的線程數是可以進行控制的,因此我們可以通過控制最大線程來使我們的伺服器打到最大的使用率,同事又可以保證及時流量突然增大也不會佔用伺服器過多的資源。

3、newScheduledThreadPool

該線程池支持定時,以及周期性的任務執行,我們可以延遲任務的執行時間,也可以設置一個周期性的時間讓任務重複執行。 該線程池中有以下兩種延遲的方法。

測試一

  1. public static void method_02() { 
  2.     ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); 
  3.  
  4.     executor.scheduleAtFixedRate(new Runnable() { 
  5.         @Override 
  6.         public void run() { 
  7.             long start = new Date().getTime(); 
  8.             System.out.println("scheduleAtFixedRate 開始執行時間:" + 
  9.                     DateFormat.getTimeInstance().format(new Date())); 
  10.             try { 
  11.                 Thread.sleep(5000); 
  12.             } catch (InterruptedException e) { 
  13.                 e.printStackTrace(); 
  14.             } 
  15.             long end = new Date().getTime(); 
  16.             System.out.println("scheduleAtFixedRate 執行花費時間=" + (end - start) / 1000 + "m"); 
  17.             System.out.println("scheduleAtFixedRate 執行完成時間:" + DateFormat.getTimeInstance().format(new Date())); 
  18.             System.out.println("======================================"); 
  19.         } 
  20.     }, 1, 5, TimeUnit.SECONDS); 

執行結果

測試二

總結:以上兩種方式不同的地方是任務的執行時間,如果間隔時間大於任務的執行時間,任務不受執行時間的影響。如果間隔時間小於任務的執行時間,那麼任務執行結束之後,會立馬執行,至此間隔時間就會被打亂。

測試一

  1. public static void method_03() { 
  2.     ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 
  3.  
  4.     executor.scheduleWithFixedDelay(new Runnable() { 
  5.         @Override 
  6.         public void run() { 
  7.             long start = new Date().getTime(); 
  8.             System.out.println("scheduleWithFixedDelay 開始執行時間:" + 
  9.                     DateFormat.getTimeInstance().format(new Date())); 
  10.             try { 
  11.                 Thread.sleep(1000); 
  12.             } catch (InterruptedException e) { 
  13.                 e.printStackTrace(); 
  14.             } 
  15.             long end = new Date().getTime(); 
  16.             System.out.println("scheduleWithFixedDelay執行花費時間=" + (end - start) / 1000 + "m"); 
  17.             System.out.println("scheduleWithFixedDelay執行完成時間:" 
  18.                     + DateFormat.getTimeInstance().format(new Date())); 
  19.             System.out.println("======================================"); 
  20.         } 
  21.     }, 1, 2, TimeUnit.SECONDS); 

執行結果

測試二

  1. public static void method_03() { 
  2.     ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 
  3.  
  4.     executor.scheduleWithFixedDelay(new Runnable() { 
  5.         @Override 
  6.         public void run() { 
  7.             long start = new Date().getTime(); 
  8.             System.out.println("scheduleWithFixedDelay 開始執行時間:" + 
  9.                     DateFormat.getTimeInstance().format(new Date())); 
  10.             try { 
  11.                 Thread.sleep(5000); 
  12.             } catch (InterruptedException e) { 
  13.                 e.printStackTrace(); 
  14.             } 
  15.             long end = new Date().getTime(); 
  16.             System.out.println("scheduleWithFixedDelay執行花費時間=" + (end - start) / 1000 + "m"); 
  17.             System.out.println("scheduleWithFixedDelay執行完成時間:" 
  18.                     + DateFormat.getTimeInstance().format(new Date())); 
  19.             System.out.println("======================================"); 
  20.         } 
  21.     }, 1, 2, TimeUnit.SECONDS); 

執行結果

總結:同樣的,跟scheduleWithFixedDelay測試方法一樣,可以測出scheduleWithFixedDelay的間隔時間不會受任務執行時間長短的影響。

4、newSingleThreadExecutor

這是一個單線程池,至始至終都由一個線程來執行。

  1. public static void method_04() { 
  2.  
  3.     ExecutorService executor = Executors.newSingleThreadExecutor(); 
  4.  
  5.     for (int i = 0; i < 5; i++) { 
  6.         final int index = i; 
  7.         executor.execute(() -> { 
  8.             try { 
  9.                 Thread.sleep(2 * 1000); 
  10.             } catch (InterruptedException e) { 
  11.                 e.printStackTrace(); 
  12.             } 
  13.             System.out.println(Thread.currentThread().getName() + "   " + index); 
  14.         }); 
  15.     } 
  16.     executor.shutdown(); 

執行結果

四、線程池的作用

線程池的作用主要是為了提升系統的性能以及使用率。文章剛開始就提到,如果我們使用最簡單的方式創建線程,如果用戶量比較大,那麼就會產生很多創建和銷毀線程的動作,這會導致伺服器在創建和銷毀線程上消耗的性能可能要比處理實際業務花費的時間和性能更多。線程池就是為了解決這種這種問題而出現的。

同樣思想的設計還有很多,比如資料庫連接池,由於頻繁的連接資料庫,然而創建連接是一個很消耗性能的事情,所有資料庫連接池就出現了。

【編輯推薦】

【責任編輯:

武曉燕

TEL:(010)68476606】

點讚 0

相關焦點

  • Java中線程池的簡單使用
    線程池就是用來管理線程的在沒有接觸線程池之前,我們使用線程的時候就去創建一個線程,然後startup()就可以假設,你現在有一個while n(n=10000)的循環,每一次循環都要啟動一個線程去計算n的因數,這樣頻繁而且大量的創建線程,系統的效率會大幅下降
  • java新手揭秘:阿里巴巴為何禁止使用Executors來創建線程池
    當一個java新手從不斷地Curd階段跳出來之後,就會學習java的並發,並行等高階用法,自然就會用到線程、線程池,線程池的好處這裡就不做詳細解釋,你應該會學習到Executors創建線程池的四個方法, 分別是:newFixedThreadPool
  • 深入理解 Java 線程池!
    這種問題使用線程池便可以很好的解決。通過線程池線程,銷毀及回收等交由線程池進行管理,就可以避免以上的問題。我們在使用過程中經常會直接使用newSingleThreadExecutor(),newCachedThreadPool(),newFixedThreadPool(int Threads)等已經封裝好的線程池,但這些都是通過ThreadPoolExecutor類中通過構造函數傳入不同的參數封裝的對象,所以想要了解線程池,我們就要認真研究一下線程池中最重要的ThreadPoolExecutor類。
  • Java入門 - - - 線程池的基本使用
    問題解決思路:我們可以用少量的線程保持工作且可以反覆執行任務(避免線程生命周期的損耗),這也就是要使用線程池。線程池的優點:加快響應速度(線程不必頻繁創建和銷毀);合理的利用CPU和內存這些有限資源;便於進行統一管理使用場景:比如伺服器接收大量請求時,或是開發過程中需要創建多個線程處理任務等。講解:核心類ThreadPoolExecutor。
  • 【線程池】java線程池ThreadPoolExecutor
    可選的參數為java.util.concurrent.TimeUnit中的幾個靜態屬性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。    workQueue 存放通過execute方法提交給線程等待執行的任務的隊列。    threadFactory 負責給線程池創建線程的工廠。
  • java線程池核心類ThreadPoolExecutor概述
    JAVA中的阻塞隊列和非阻塞隊列我們介紹了常用的幾種隊列,隊列的使用很廣泛,特別是一些需要生產消費模式的場景以及需要對全局的集合進行操作的場景。我們使用線程池來解決這個問題,讓線程運行完不立即銷毀,並且重複使用,繼續執行其他的任務。使用線程池來管理線程,一方面使線程的創建更加規範,可以合理控制開闢線程的數量;另一方面線程的細節管理交給線程池處理,優化了資源的開銷。
  • Java中線程池,你真的會用嗎?
    線程池的實現原理》這篇文章中,我們介紹過了Java中線程池的常見用法以及基本原理。在文中有這樣一段描述:可以通過Executors靜態工廠構建線程池,但一般不建議這樣使用。關於這個問題,在那篇文章中並沒有深入的展開。作者之所以這麼說,是因為這種創建線程池的方式有很大的隱患,稍有不慎就有可能導致線上故障。本文我們就來圍繞這個問題來分析一下為什麼JDK自身提供的構建線程池的方式並不建議使用?
  • Java之線程池的簡單介紹
    通過使用線程池,可以使線程復用,就是執行完一個任務,不銷毀,繼續執行其他任務。其實線程池就相當於一個容器(集合),裡面有很多線程。現在小編來說說線程池的代碼實現:首先,java.util.concurrent.Executors:線程池的工廠類,用來生成線程池其次,Executors類中的靜態方法:
  • 使用Executors,ThreadPoolExecutor,創建線程池,源碼分析理解
    當然 Executors 也是用不同的參數去 new ThreadPoolExecutor 實現的,本文先分析前四種線程創建方式,後在分析 new ThreadPoolExecutor 創建方式使用 Executors 創建線程池1.newFixedThreadPool()由於使用了LinkedBlockingQueue所以maximumPoolSize
  • 手寫Java線程池(超詳細解說)
    線程池原理使用隊列創建一定數量的線程, 當有任務的時候, 使用隊列中線程執行任務(如果任務過多, 就將其放入任務隊列, 進入等待執行狀態), 任務執行完就自動回收線程隊列中的線程(任務過少或者任務數量小於線程數量, 超出的線程將會銷毀, 做到線程隊列具有伸縮性);根據上面描述, 我們自己的線程池將具有一下特點:1.內部使用隊列來管理線程, 管理提交的任務.2
  • 深入理解 Java 線程池,講解的太清晰了
    正是由於這個問題,所以有必要引入線程池。使用 線程池的好處有以下幾點:降低資源消耗 - 通過重複利用已創建的線程降低線程創建和銷毀造成的消耗。提高線程的可管理性 - 線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。但是要做到合理的利用線程池,必須對其原理了如指掌。
  • 從使用到原理,探究Java線程池
    什麼是線程池當我們需要處理某個任務的時候,可以新創建一個線程,讓線程去執行任務。線程池的字面意思就是存放線程的池子,當我們需要處理某個任務的時候,可以從線程池裡取出一條線程去執行。線程池的使用1.線程池的核心參數想掌握線程池首先要理解線程池構造函數的參數:逐個解釋這些參數是很難理解的,這裡我結合一張線程池處理的流程圖進行講解:
  • Java線程池詳解及常用方法
    這篇將介紹一下線程池的基本使用。ExecutorsExecutors是concurrent包下的一個類,為我們提供了創建線程池的簡便方法。Executors可以創建我們常用的四種線程池:(1)newCachedThreadPool 創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。不設上限,提交的任務將立即執行。
  • 通過源碼解析,深入Java 線程池原理
    復用已創建好的線程可以提高系統的性能,藉助池化技術的思想,通過預先創建好多個線程,放在池中,這樣可以在需要使用線程的時候直接獲取,避免多次重複創建、銷毀帶來的開銷。(1)線程池的優點線程是稀缺資源,使用線程池可以減少創建和銷毀線程的次數,每個工作線程都可以重複使用。
  • 「原創」Java並發編程系列33|深入理解線程池(上)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫並發編程必不可少的線程池,接下來分兩篇文章介紹線程池,本文是第一篇。介紹1.1 使用場景並發編程可以高效利用CPU資源,提升任務執行效率,但是多線程及線程間的切換也伴隨著資源的消耗。當遇到單個任務處理時間比較短,但需要處理的任務數量很大時,線程會頻繁的創建銷毀,大量的時間和資源都會浪費在線程的創建和銷毀上,效率很低。
  • 七個方面帶你玩轉Java線程池
    針對這種情況,我們需要使用線程池來管理線程,帶來的好處有3個:  ① 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷毀造成的消耗。② 提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。③ 提高線程的可管理性。線程是稀缺資源,不能無限制創建,否則不但會消耗資源,還會降低系統的穩定性,而使用線程池可以進行統一分配、調優和監控。
  • java開發之Tomcat線程池優化
    前言:上期我們說到了jvm的內存優化,這期我們來說說tomcat的線程池優化,此思路同樣可用於c3p0等連接池tomcat線程池優化我們tomcat線程池的優化,其實就是最大限度的發揮tomcat的性能。即讓伺服器在保障性能的情況下並發最大並發:所有線程,在同一秒一起訪問同一個資源。
  • 帶你一步步從源碼角度深入理解Java線程池(簡單易懂)
    1、使用線程池的好處:提高響應速度(減少了創建新線程的時間)降低資源消耗(重複利用線程池中線程,不需要每次都創建)便於線程進行管理,線程是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控。
  • java開發基礎 線程池詳解
    前言:我們在開發中經常用會到多線程,今天來介紹一下線程池的使用。線程池簡介1.5以後引入了Executor框架,它的內部使用了線程池機制,我們常使用這個框架來創建線程池。下圖為Executor下的接口和類繼承關係:Executor框架我們常用到的包括:線程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等Executors提供了一系列工廠方法用於創建線程池,返回的線程池都實現了ExecutorService接口。
  • Java並發編程系列34|深入理解線程池(下)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文是深入理解線程池下篇:線程池介紹Executor框架接口線程池狀態線程池參數線程池創建執行過程關閉線程池其他問題任務拒絕策略線程池中的線程初始化線程池容量的動態調整線程池的監控6.