xylz,imxylz

          關(guān)注后端架構(gòu)、中間件、分布式和并發(fā)編程

             :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            111 隨筆 :: 10 文章 :: 2680 評(píng)論 :: 0 Trackbacks

           [本文地址:http://www.aygfsteel.com/Files/xylz/Inside.Java.Concurrency_33.ThreadPool.part6_ThreadFactory_Worker.pdf]


          線程池?cái)?shù)據(jù)結(jié)構(gòu)與線程構(gòu)造方法

          由于已經(jīng)看到了ThreadPoolExecutor的源碼,因此很容易就看到了ThreadPoolExecutor線程池的數(shù)據(jù)結(jié)構(gòu)。圖1描述了這種數(shù)據(jù)結(jié)構(gòu)。

          ThreadPoolExecutor

          圖1 ThreadPoolExecutor 數(shù)據(jù)結(jié)構(gòu)

          其實(shí),即使沒(méi)有上述圖形描述ThreadPoolExecutor的數(shù)據(jù)結(jié)構(gòu),我們根據(jù)線程池的要求也很能夠猜測(cè)出其數(shù)據(jù)結(jié)構(gòu)出來(lái)。

          • 線程池需要支持多個(gè)線程并發(fā)執(zhí)行,因此有一個(gè)線程集合Collection<Thread>來(lái)執(zhí)行線程任務(wù);
          • 涉及任務(wù)的異步執(zhí)行,因此需要有一個(gè)集合來(lái)緩存任務(wù)隊(duì)列Collection<Runnable>;
          • 很顯然在多個(gè)線程之間協(xié)調(diào)多個(gè)任務(wù),那么就需要一個(gè)線程安全的任務(wù)集合,同時(shí)還需要支持阻塞、超時(shí)操作,那么BlockingQueue是必不可少的;
          • 既然是線程池,出發(fā)點(diǎn)就是提高系統(tǒng)性能同時(shí)降低資源消耗,那么線程池的大小就有限制,因此需要有一個(gè)核心線程池大小(線程個(gè)數(shù))和一個(gè)最大線程池大小(線程個(gè)數(shù)),有一個(gè)計(jì)數(shù)用來(lái)描述當(dāng)前線程池大小;
          • 如果是有限的線程池大小,那么長(zhǎng)時(shí)間不使用的線程資源就應(yīng)該銷(xiāo)毀掉,這樣就需要一個(gè)線程空閑時(shí)間的計(jì)數(shù)來(lái)描述線程何時(shí)被銷(xiāo)毀;
          • 前面描述過(guò)線程池也是有生命周期的,因此需要有一個(gè)狀態(tài)來(lái)描述線程池當(dāng)前的運(yùn)行狀態(tài);
          • 線程池的任務(wù)隊(duì)列如果有邊界,那么就需要有一個(gè)任務(wù)拒絕策略來(lái)處理過(guò)多的任務(wù),同時(shí)在線程池的銷(xiāo)毀階段也需要有一個(gè)任務(wù)拒絕策略來(lái)處理新加入的任務(wù);
          • 上面種的線程池大小、線程空閑實(shí)際那、線程池運(yùn)行狀態(tài)等等狀態(tài)改變都不是線程安全的,因此需要有一個(gè)全局的鎖(mainLock)來(lái)協(xié)調(diào)這些競(jìng)爭(zhēng)資源;
          • 除了以上數(shù)據(jù)結(jié)構(gòu)以外,ThreadPoolExecutor還有一些狀態(tài)用來(lái)描述線程池的運(yùn)行計(jì)數(shù),例如線程池運(yùn)行的任務(wù)數(shù)、曾經(jīng)達(dá)到的最大線程數(shù),主要用于調(diào)試和性能分析。

           

          對(duì)于ThreadPoolExecutor而言,一個(gè)線程就是一個(gè)Worker對(duì)象,它與一個(gè)線程綁定,當(dāng)Worker執(zhí)行完畢就是線程執(zhí)行完畢,這個(gè)在后面詳細(xì)討論線程池中線程的運(yùn)行方式。

          既然是線程池,那么就首先研究下線程的構(gòu)造方法。

          public interface ThreadFactory {
              Thread newThread(Runnable r);
          }

           

          ThreadPoolExecutor使用一個(gè)線程工廠來(lái)構(gòu)造線程。線程池都是提交一個(gè)任務(wù)Runnable,然后在某一個(gè)線程Thread中執(zhí)行,ThreadFactory 負(fù)責(zé)如何創(chuàng)建一個(gè)新線程。

          在J.U.C中有一個(gè)通用的線程工廠java.util.concurrent.Executors.DefaultThreadFactory,它的構(gòu)造方式如下:

          static class DefaultThreadFactory implements ThreadFactory {
             
          static final AtomicInteger poolNumber = new AtomicInteger(1);
             
          final ThreadGroup group;
             
          final AtomicInteger threadNumber = new AtomicInteger(1);
             
          final String namePrefix;
              DefaultThreadFactory() {
                  SecurityManager s
          = System.getSecurityManager();
                  group
          = (s != null)? s.getThreadGroup() :
                                       Thread.currentThread().getThreadGroup();
                  namePrefix
          = "pool-" +
                                poolNumber.getAndIncrement()
          +
                              
          "-thread-";
              }
             
          public Thread newThread(Runnable r) {
                  Thread t
          = new Thread(group, r,
                                        namePrefix
          + threadNumber.getAndIncrement(),
                                       
          0);
                 
          if (t.isDaemon())
                      t.setDaemon(
          false);
                 
          if (t.getPriority() != Thread.NORM_PRIORITY)
                      t.setPriority(Thread.NORM_PRIORITY);
                 
          return t;
              }
          }

           

          在這個(gè)線程工廠中,同一個(gè)線程池的所有線程屬于同一個(gè)線程組,也就是創(chuàng)建線程池的那個(gè)線程組,同時(shí)線程池的名稱都是“pool-<poolNum>-thread-<threadNum>”,其中poolNum是線程池的數(shù)量序號(hào),threadNum是此線程池中的線程數(shù)量序號(hào)。這樣如果使用jstack的話很容易就看到了系統(tǒng)中線程池的數(shù)量和線程池中線程的數(shù)量。另外對(duì)于線程池中的所有線程默認(rèn)都轉(zhuǎn)換為非后臺(tái)線程,這樣主線程退出時(shí)不會(huì)直接退出JVM,而是等待線程池結(jié)束。還有一點(diǎn)就是默認(rèn)將線程池中的所有線程都調(diào)為同一個(gè)級(jí)別,這樣在操作系統(tǒng)角度來(lái)看所有系統(tǒng)都是公平的,不會(huì)導(dǎo)致競(jìng)爭(zhēng)堆積。

          線程池中線程生命周期

          一個(gè)線程Worker被構(gòu)造出來(lái)以后就開(kāi)始處于運(yùn)行狀態(tài)。以下是一個(gè)線程執(zhí)行的簡(jiǎn)版邏輯。

          private final class Worker implements Runnable {
             
          private final ReentrantLock runLock = new ReentrantLock();
             
          private Runnable firstTask;
              Thread thread;
              Worker(Runnable firstTask) {
                 
          this.firstTask = firstTask;
              }
             
          private void runTask(Runnable task) {
                 
          final ReentrantLock runLock = this.runLock;
                  runLock.lock();
                 
          try {
                     task.run();
                  }
          finally {
                      runLock.unlock();
                  }
              }
             
          public void run() {
                 
          try {
                      Runnable task
          = firstTask;
                      firstTask
          = null;
                     
          while (task != null || (task = getTask()) != null) {
                          runTask(task);
                          task
          = null;
                      }
                  }
          finally {
                      workerDone(
          this);
                  }
              }
          }

           

          ThreadPoolExecutor-Worker

          當(dāng)提交一個(gè)任務(wù)時(shí),如果需要?jiǎng)?chuàng)建一個(gè)線程(何時(shí)需要在下一節(jié)中探討)時(shí),就調(diào)用線程工廠創(chuàng)建一個(gè)線程,同時(shí)將線程綁定到Worker工作隊(duì)列中。需要說(shuō)明的是,Worker隊(duì)列構(gòu)造的時(shí)候帶著一個(gè)任務(wù)Runnable,因此Worker創(chuàng)建時(shí)總是綁定著一個(gè)待執(zhí)行任務(wù)。換句話說(shuō),創(chuàng)建線程的前提是有必要?jiǎng)?chuàng)建線程(任務(wù)數(shù)已經(jīng)超出了線程或者強(qiáng)制創(chuàng)建新的線程,至于為何強(qiáng)制創(chuàng)建新的線程后面章節(jié)會(huì)具體分析),不會(huì)無(wú)緣無(wú)故創(chuàng)建一堆空閑線程等著任務(wù)。這是節(jié)省資源的一種方式。

          一旦線程池啟動(dòng)線程后(調(diào)用線程run())方法,那么線程工作隊(duì)列Worker就從第1個(gè)任務(wù)開(kāi)始執(zhí)行(這時(shí)候發(fā)現(xiàn)構(gòu)造Worker時(shí)傳遞一個(gè)任務(wù)的好處了),一旦第1個(gè)任務(wù)執(zhí)行完畢,就從線程池的任務(wù)隊(duì)列中取出下一個(gè)任務(wù)進(jìn)行執(zhí)行。循環(huán)如此,直到線程池被關(guān)閉或者任務(wù)拋出了一個(gè)RuntimeException。

          由此可見(jiàn),線程池的基本原理其實(shí)也很簡(jiǎn)單,無(wú)非預(yù)先啟動(dòng)一些線程,線程進(jìn)入死循環(huán)狀態(tài),每次從任務(wù)隊(duì)列中獲取一個(gè)任務(wù)進(jìn)行執(zhí)行,直到線程池被關(guān)閉。如果某個(gè)線程因?yàn)閳?zhí)行某個(gè)任務(wù)發(fā)生異常而終止,那么重新創(chuàng)建一個(gè)新的線程而已。如此反復(fù)。

          其實(shí),線程池原理看起來(lái)簡(jiǎn)單,但是復(fù)雜的是各種策略,例如何時(shí)該啟動(dòng)一個(gè)線程,何時(shí)該終止、掛起、喚醒一個(gè)線程,任務(wù)隊(duì)列的阻塞與超時(shí),線程池的生命周期以及任務(wù)拒絕策略等等。下一節(jié)將研究這些策略問(wèn)題。

           

           



          ©2009-2014 IMXYLZ |求賢若渴
          posted on 2011-01-18 23:43 imxylz 閱讀(16111) 評(píng)論(6)  編輯  收藏 所屬分類: Java Concurrency

          評(píng)論

          # re: 深入淺出 Java Concurrency (33): 線程池 part 6 線程池的實(shí)現(xiàn)及原理 (1) 2011-01-19 02:36 電腦知識(shí)與技術(shù)博客
          真是太好了。非常詳細(xì)的技術(shù)文檔  回復(fù)  更多評(píng)論
            

          # re: 深入淺出 Java Concurrency (33): 線程池 part 6 線程池的實(shí)現(xiàn)及原理 (1)[未登錄](méi) 2011-01-20 05:46 xiaoxiao
          thanks!  回復(fù)  更多評(píng)論
            

          # re: 深入淺出 Java Concurrency (33): 線程池 part 6 線程池的實(shí)現(xiàn)及原理 (1) 2011-03-30 15:38 現(xiàn)金流量表的編制方法
          真是太詳細(xì)了  回復(fù)  更多評(píng)論
            

          # re: 深入淺出 Java Concurrency (33): 線程池 part 6 線程池的實(shí)現(xiàn)及原理 (1)[未登錄](méi) 2012-05-09 12:15 zym
          很感謝總結(jié)與分享,這個(gè)系列收獲很多  回復(fù)  更多評(píng)論
            

          # re: 深入淺出 Java Concurrency (33): 線程池 part 6 線程池的實(shí)現(xiàn)及原理 (1)[未登錄](méi) 2014-05-08 11:31 nemo
          看完了這一篇,很詳細(xì)。

          有一個(gè)問(wèn)題就是:Worker的數(shù)量是如何確定的,由哪個(gè)參數(shù)確定?

          Worker是在什么時(shí)候被構(gòu)造出來(lái)的?   回復(fù)  更多評(píng)論
            

          # re: 深入淺出 Java Concurrency (33): 線程池 part 6 線程池的實(shí)現(xiàn)及原理 (1) 2014-05-08 11:56 imxylz
          @nemo

          當(dāng)提交一個(gè)任務(wù)時(shí),如果需要?jiǎng)?chuàng)建一個(gè)線程(何時(shí)需要在下一節(jié)中探討)時(shí),就調(diào)用線程工廠創(chuàng)建一個(gè)線程,同時(shí)將線程綁定到Worker工作隊(duì)列中。

          線程池有兩個(gè)核心變量:corePoolSize與maximumPoolSize。

          下一頁(yè)描述的比較詳細(xì):http://www.aygfsteel.com/xylz/archive/2011/02/11/344091.html  回復(fù)  更多評(píng)論
            


          ©2009-2014 IMXYLZ
          主站蜘蛛池模板: 兰考县| 资溪县| 东乌珠穆沁旗| 旌德县| 连山| 凌源市| 隆子县| 姜堰市| 曲松县| 正定县| 萝北县| 望奎县| 翁源县| 石门县| 蒙阴县| 保靖县| 扎赉特旗| 盐城市| 永清县| 永寿县| 佛冈县| 灵璧县| 宁蒗| 兴义市| 桐庐县| 台中市| 东安县| 饶阳县| 宣威市| 读书| 平江县| 苗栗市| 浑源县| 鹤庆县| 三都| 沙河市| 阜平县| 孝义市| 呼伦贝尔市| 罗平县| 巧家县|