【學習筆記】多線程編程-線程池/任務/線程

來源:互聯網
上載者:User

寫在前面的話:之前一直沒瞭解過多線程編程相關的東西,對於線程之類相關的概念也是一知半解。這次藉著準備面試的時機,計劃將一些關鍵性的盲區(除多線程以外,還有記憶體回收、圖片緩衝、效能最佳化等等)都徹底掃一遍。接下來我會陸續整理成學習筆記,主要作為自己學習後的溫習鞏固之用。如果能協助到同樣迷茫的小白的話,深感榮幸;如果因為缺少實戰導致理解有誤或解釋不清的地方,還請路過的大牛不吝賜教,共同進步^_^

知識點相關

作為這次學習的切入點,線程池關聯到的概念有很多,比如線程到底是什麼,常搭配使用的Runnable又是啥,為什麼又要搭配起來使用,等等。

關於這些,我找到了一篇博文:多線程(Thread、線程建立、線程池),裡面的解釋說明非常全面了,感興趣的可以整篇通讀,一定受益匪淺。我在這裡僅把幾個重要且基礎的點列出來,按自己的理解簡要說明一下:

  • 線程Thread類:用於建立線程(start方法)和定義線程所執行的任務(run方法),這裡的run方法與主線程中的main方法等效。
  • 任務Runnable介面:一般有兩種方法定義任務,一種是繼承Thread類重寫run方法,另一種則是實現Runnable介面重寫run方法。後者用的比較多,是常用搭配,目的就在於將線程建立和線程任務這兩塊解耦
  • 任務Callable介面:作用與Runnable類似,call方法也與run方法地位相同,區別在於Callable返回結果而Runnable不返回
  • 任務結果Future介面:對應的通過Future介面的get方法來擷取Callable返回的結果,注意:使用get方法時如還未得到結果則會阻塞當前線程直至拿到結果
  • 任務FutureTask類:可以理解為是Runnable/Callable和Future的結合體,通過在Callable(Runnable也會先轉為Callable)外面加包一層,使用get方法直接擷取任務結果

除了以上這些涉及到的類,還有一些多線程相關的概念列舉如下:

  • 進程:簡單來說,一個應用對應一個進程,佔用一定記憶體
  • 線程:一個進程包含多個線程,線程相當於是任務執行單元,互不干擾
  • 搶佔式調度:按優先順序高低分配向線程分配記憶體資源;多線程並不提高程式運行速度,而是在於提高運行效率,提高CPU的使用率
  • 主線程:運行main方法所在的線程即為主線程;通常將耗時操作放到主線程以外的線程,以防阻塞主線程

準備工作完畢,下面重點講線程池↓

為什麼要使用線程池

簡單來說,線程池就像一個管家,通過調度和合理分配工作,讓每個傭人(即線程)在繁忙時都能各司其職高效完成任務,同時又能在空閑時得到充分休息。更具體來說,主要有兩點:一是合理利用現存的線程,減少線程建立和銷毀所消耗的資源開銷,加快響應;二是便於監督和管理每個線程的生命週期。

線程池ThreadPoolExecutor類

要搞清楚這個類,先上一張圖。

這是一張繼承關係圖。最初的Executor介面定義的是execute方法,比較簡單。它的子類ExecutorService介面,你可以理解為是通用線程池類,裡面增加了線程池常用的一些用於控制線程的方法,如submit和shutdown等,用於管理生命週期。繼承的AbstractExecutorService抽象類別,是在ExecutorService基礎上的預設實現。最後,ThreadPoolExecutor繼承自該抽象類別,通過構造方法來配置線程池。

這裡還要提一下Executors類,不在圖中,是線程池工廠類,可以通過一系列靜態方法建立線程池。查看源碼可以發現,方法內部都是使用ThreadPoolExecutor的構造方法加上不同的配置參數來實現。

下面是ThreadPoolExecutor類的幾個構造方法:

重點講第三個,其他的構造方法基本類似。其中的參數有(按順序依次):核心線程數N,最大線程數M,逾時時間T,時間單位U,緩衝隊列B和拒絕處理Handler。這些參數各自代表的含義,將結合不同的緩衝隊列類型分情況講解:

  • SynchronousQueue類型:所有任務不進入緩衝隊列(可以理解為緩衝隊列的容量為0),直接進入可用線程執行,當無可用線程時則直接建立新線程執行任務。當線程數超過M時,調用Handler處理被拒絕的新任務。
  • LinkedBlockingQueue類型:有新任務時,若當前線程數少於N,則建立新線程;若大於等於N,則進入緩衝隊列;當緩衝隊列充滿時,且線程數少於M,則繼續建立新線程,直至達到M時調用Handler處理拒絕任務。一般情況下,LinkedBlockingQueue作為無界隊列不指定緩衝容量,可以無限擴充,此時相當於M失效;但也可以通過建構函式來指定容量。
  • ArrayBlockingQueue類型:規則與LinkedBlockingQueue類似,但一般情況下指定隊列長度。

具體這三種隊列類型的區別可以參考這篇文章:ThreadPoolExecutor的三種隊列

還有逾時時間T和時間單位U,主要作用在於令非核心線程閑置超過一定時間長度後自動銷毀,從而釋放資源。

拒絕任務Handler

當線程數和緩衝隊列都達到飽和時,會調用相應的Handler處理拒絕的任務。以下是四種預定義的處理策略: 

  • 在預設的AbortPolicy 中,拒絕後直接拋出異常RejectedExecutionException。
  • 在CallerRunsPolicy 中,直接在 execute 方法的調用線程中運行被拒絕的任務。該策略提供簡單的反饋控制機制,能夠減緩新任務的提交速度。
  • 在DiscardPolicy 中,拒絕後直接放棄任務。
  • 在DiscardOldestPolicy 中,位於工作隊列頭部(即最舊)的任務將被放棄,然後重試執行程式(如果再次失敗,則重複此過程)。
線程池的簡單樣本

以斐波那契數列為例,主要展示了不同任務類型的用法區別,注意此處使用submit而不是execute來提交任務,兩者區別類似Callable和Runnable。

 1 package com.effective.java.concurrent.task;   2    3 import java.util.concurrent.Callable;   4 import java.util.concurrent.ExecutionException;   5 import java.util.concurrent.ExecutorService;   6 import java.util.concurrent.Executors;   7 import java.util.concurrent.Future;   8 import java.util.concurrent.FutureTask;   9    10 public class RunnableFutureTask {  11   12     static ExecutorService mExecutor =  new ThreadPoolExecutor(2, 3, 5, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());  13   14   15     public static void main(String[] args) {  16         futureDemo();  17     }  18   19 20     static void futureDemo() {  21         try {  22             /** 23              * 提交runnable則沒有傳回值, future沒有資料 24              */  25             Future<?> result = mExecutor.submit(new Runnable() {  26   27                 @Override  28                 public void run() {  29                     fibc(20);  30                 }  31             });  32   33             System.out.println("future result from runnable : " + result.get());  34   35             /** 36              * 提交Callable, 有傳回值, future中能夠擷取傳回值 37              */  38             Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() {  39                 @Override  40                 public Integer call() throws Exception {  41                     return fibc(20);  42                 }  43             });  44   45             System.out  46                     .println("future result from callable : " + result2.get());  47   48             /** 49              * FutureTask可以通過Thread封裝來直接執行,也可以提交給ExecuteService來執行 50              * ,並且還可以通過v get()返回執行結果,線上程體沒有執行完成的時候,主線程一直阻塞等待,執行完則直接返回結果。 51              */  52             FutureTask<Integer> futureTask = new FutureTask<Integer>(  53                     new Callable<Integer>() {  54                         @Override  55                         public Integer call() throws Exception {  56                             return fibc(20);  57                         }  58                     });  59 60             // 提交futureTask  61             mExecutor.submit(futureTask) ;  62             System.out.println("future result from futureTask : "  63                     + futureTask.get());  64   65         } catch (InterruptedException e) {  66             e.printStackTrace();  67         } catch (ExecutionException e) {  68             e.printStackTrace();  69         }  70     }  71   72     /** 73      * 效率底下的斐波那契數列, 耗時的操作 74      *  75      * @param num 76      * @return 77      */  78     static int fibc(int num) {  79         if (num == 0) {  80             return 0;  81         }  82         if (num == 1) {  83             return 1;  84         }  85         return fibc(num - 1) + fibc(num - 2);  86     }   87 }  

 

除文中提到的博文外,其餘參考博文列舉如下,感謝各位博主的無私分享^_^:

java並發編程--Executor架構

一心多用多線程-線程池ThreadPoolExecutor-看這篇就夠了

一心多用多線程-細談java線程池submit與execute的區別

Java多線程-線程池ThreadPoolExecutor構造方法和規則

深入理解Callable

Java中的Runnable、Callable、Future、FutureTask的區別與樣本

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.