多線程2,線程池深入理解

目錄介紹

  • 1.ThreadPoolExecutor類介紹
  • 1.1 構造函數
  • 1.2 參數解析
  • 1.3 遵循的規則
  • 1.4 使用線程池管理線程的優點
  • 2.關于線程池的分類
  • 2.1 FixedThreadPool
  • 2.2 CachedThreadPool
  • 2.3 ScheduledThreadPool
  • 2.4 SingleThreadExecutor
  • 3.線程池一般用法
  • 3.1 一般方法介紹
  • 3.2 newFixedThreadPool的使用
  • 3.3 newSingleThreadExecutor的使用
  • 3.4 newCachedThreadPool的使用
  • 3.5 newScheduledThreadPool的使用
  • 3.6 線程創建規則
  • 4.線程池封裝
  • 4.1 具體可以參考下篇文章
  • 4.2 參考博客

好消息

  • 博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計47篇[近20萬字],轉載請注明出處,謝謝!
  • 鏈接地址:https://github.com/yangchong211/YCBlogs
  • 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起于忽微,量變引起質變!

前言介紹

1.ThreadPoolExecutor類介紹

1.1 構造函數

  • ExecutorService是最初的線程池接口,ThreadPoolExecutor類是對線程池的具體實現,它通過構造方法來配置線程池的參數
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

1.2 參數解析

  • corePoolSize,線程池中核心線程的數量,默認情況下,即使核心線程沒有任務在執行它也存在的,我們固定一定數量的核心線程且它一直存活這樣就避免了一般情況下CPU創建和銷毀線程帶來的開銷。我們如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置為true,那么閑置的核心線程就會有超時策略,這個時間由keepAliveTime來設定,即keepAliveTime時間內如果核心線程沒有回應則該線程就會被終止。allowCoreThreadTimeOut默認為false,核心線程沒有超時時間。
  • maximumPoolSize,線程池中的最大線程數,當任務數量超過最大線程數時其它任務可能就會被阻塞。最大線程數=核心線程+非核心線程。非核心線程只有當核心線程不夠用且線程池有空余時才會被創建,執行完任務后非核心線程會被銷毀。
  • keepAliveTime,非核心線程的超時時長,當執行時間超過這個時間時,非核心線程就會被回收。當allowCoreThreadTimeOut設置為true時,此屬性也作用在核心線程上。
  • unit,枚舉時間單位,TimeUnit。
  • workQueue,線程池中的任務隊列,我們提交給線程池的runnable會被存儲在這個對象上。

1.3 遵循的規則

  • 當線程池中的核心線程數量未達到最大線程數時,啟動一個核心線程去執行任務;
  • 如果線程池中的核心線程數量達到最大線程數時,那么任務會被插入到任務隊列中排隊等待執行;
  • 如果在上一步驟中任務隊列已滿但是線程池中線程數量未達到限定線程總數,那么啟動一個非核心線程來處理任務;
  • 如果上一步驟中線程數量達到了限定線程總量,那么線程池則拒絕執行該任務,且ThreadPoolExecutor會調用RejectedtionHandler的rejectedExecution方法來通知調用者。

1.4 使用線程池管理線程的優點

  • 1、線程的創建和銷毀由線程池維護,一個線程在完成任務后并不會立即銷毀,而是由后續的任務復用這個線程,從而減少線程的創建和銷毀,節約系統的開銷
  • 2、線程池旨在線程的復用,這就可以節約我們用以往的方式創建線程和銷毀所消耗的時間,減少線程頻繁調度的開銷,從而節約系統資源,提高系統吞吐量
  • 3、在執行大量異步任務時提高了性能
  • 4、Java內置的一套ExecutorService線程池相關的api,可以更方便的控制線程的最大并發數、線程的定時任務、單線程的順序執行等

2.關于線程池的分類

2.1 FixedThreadPool

  • 通過Executors的newFixedThreadPool()方法創建,它是個線程數量固定的線程池,該線程池的線程全部為核心線程,它們沒有超時機制且排隊任務隊列無限制,因為全都是核心線程,所以響應較快,且不用擔心線程會被回收。

2.2 CachedThreadPool

  • 通過Executors的newCachedThreadPool()方法來創建,它是一個數量無限多的線程池,它所有的線程都是非核心線程,當有新任務來時如果沒有空閑的線程則直接創建新的線程不會去排隊而直接執行,并且超時時間都是60s,所以此線程池適合執行大量耗時小的任務。由于設置了超時時間為60s,所以當線程空閑一定時間時就會被系統回收,所以理論上該線程池不會有占用系統資源的無用線程。

2.3 ScheduledThreadPool

  • 通過Executors的newScheduledThreadPool()方法來創建,ScheduledThreadPool線程池像是上兩種的合體,它有數量固定的核心線程,且有數量無限多的非核心線程,但是它的非核心線程超時時間是0s,所以非核心線程一旦空閑立馬就會被回收。這類線程池適合用于執行定時任務和固定周期的重復任務。

2.4 SingleThreadExecutor

  • 通過Executors的newSingleThreadExecutor()方法來創建,它內部只有一個核心線程,它確保所有任務進來都要排隊按順序執行。它的意義在于,統一所有的外界任務到同一線程中,讓調用者可以忽略線程同步問題。

3.線程池一般用法

3.1 一般方法介紹

  • shutDown(),關閉線程池,需要執行完已提交的任務;
  • shutDownNow(),關閉線程池,并嘗試結束已提交的任務;
  • allowCoreThreadTimeOut(boolen),允許核心線程閑置超時回收;
  • execute(),提交任務無返回值;
  • submit(),提交任務有返回值;

3.2 newFixedThreadPool的使用

  • 3.2.1 創建一個newFixedThreadPool線程池
private void newFixedThreadPool() {
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
    for (int i = 1; i <= 20; i++) {
        final int index = i;
        fixedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                Log.e("瀟湘劍雨", "線程:"+threadName+",正在執行第" + index + "個任務");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
  • 3.2.2 打印日志如下
  • 創建了一個線程數為5的固定線程數量的線程池,同理該線程池支持的線程最大并發數也是5,模擬20個任務讓它處理,執行任務。最后我們獲取線程的信息,打印日志。
  • 日志如圖所示:


    image

3.3 newSingleThreadExecutor的使用

  • 3.3.1 創建一個newSingleThreadExecutor線程池
private void newSingleThreadExecutor() {
    ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
    for (int i = 1; i <= number; i++) {
        final int index = i;
        singleThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                Log.v("瀟湘劍雨", "線程:"+threadName+",正在執行第" + index + "個任務");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
  • 3.3.2 打印日志如下
  • 改了線程池的實現方式,即依次一個一個的處理任務,而且都是復用一個線程,日志為


    image

3.4 newCachedThreadPool的使用

  • 3.4.1 創建一個newCachedThreadPool線程池
private void newCachedThreadPool() {
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 1; i <= number; i++) {
        final int index = i;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                Log.v("瀟湘劍雨newCachedThreadPool", "線程:" + threadName + ",正在執行第" + index + "個任務");
                try {
                    long time = index * 500;
                    Thread.sleep(time);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
  • 3.4.2 打印日志如下
  • 為了體現該線程池可以自動根據實現情況進行線程的重用,而不是一味的創建新的線程去處理任務,我設置了每隔1s去提交一個新任務,這個新任務執行的時間也是動態變化的,所以,效果為:


    image

3.5 newScheduledThreadPool的使用

  • 3.5.1 創建一個newScheduledThreadPool線程池
private void newScheduledThreadPool() {
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    //延遲2秒后執行該任務
    scheduledThreadPool.schedule(new Runnable() {
        @SuppressLint("LongLogTag")
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            Log.e("瀟湘劍雨newScheduledThreadPool", "線程:" + threadName + ",正在執行");
        }
    }, 2, TimeUnit.SECONDS);
    //延遲1秒后,每隔2秒執行一次該任務
    scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            Log.e("瀟湘劍雨", "線程:" + threadName + ",正在執行");
        }
    }, 1, 2, TimeUnit.SECONDS);
}
  • 3.5.2 打印日志如下
  • 通過日志可以發現schedule方法的任務只是執行了一次,然后每隔2秒執行一次該scheduleAtFixedRate方法中的任務


    image

3.6 線程創建規則

  • ThreadPoolExecutor對象初始化時,不創建任何執行線程,當有新任務進來時,才會創建執行線程。構造ThreadPoolExecutor對象時,需要配置該對象的核心線程池大小和最大線程池大小
    1. 當目前執行線程的總數小于核心線程大小時,所有新加入的任務,都在新線程中處理。
    1. 當目前執行線程的總數大于或等于核心線程時,所有新加入的任務,都放入任務緩存隊列中。
    1. 當目前執行線程的總數大于或等于核心線程,并且緩存隊列已滿,同時此時線程總數小于線程池的最大大小,那么創建新線程,加入線程池中,協助處理新的任務。
    1. 當所有線程都在執行,線程池大小已經達到上限,并且緩存隊列已滿時,就rejectHandler拒絕新的任務。

4.線程池封裝

4.1 具體可以參考下篇文章

4.2 參考博客

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容

  • 為什么使用線程池 當我們在使用線程時,如果每次需要一個線程時都去創建一個線程,這樣實現起來很簡單,但是會有一個問題...
    閩越布衣閱讀 4,303評論 10 45
  • 【JAVA 線程】 線程 進程:是一個正在執行中的程序。每一個進程執行都有一個執行順序。該順序是一個執行路徑,或者...
    Rtia閱讀 2,776評論 2 20
  • 深入分析線程池 在前面的文章中,我們使用線程的時候就去創建一個線程,這樣實現起來非常簡便,但是就會有一個問題: 如...
    史路比閱讀 460評論 0 1
  • 1、定義一個事件: 2、對UITextField控件添加監聽事件:
    遠行客丶閱讀 240評論 0 0
  • 早早就醒了,天還沒亮,窗前黑黑的。 翻個身繼續睡,可是事實證明確實醒了。索性上了廁所,看見熱水器開著,又起身關掉。...
    六月微塵閱讀 233評論 0 0