<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    kxbin
    成功留給有準(zhǔn)備的人
    posts - 10,  comments - 35,  trackbacks - 0

    很多開發(fā)者談到Java多線程開發(fā),僅僅停留在new Thread(...).start()或直接使用Executor框架這個層面,對于線程的管理和控制卻不夠深入,通過讀《Java并發(fā)編程實踐》了解到了很多不為我知但又非常重要的細(xì)節(jié),今日整理如下。

    不應(yīng)用線程池的缺點

    有些開發(fā)者圖省事,遇到需要多線程處理的地方,直接new Thread(...).start(),對于一般場景是沒問題的,但如果是在并發(fā)請求很高的情況下,就會有些隱患:

    • 新建線程的開銷。線程雖然比進(jìn)程要輕量許多,但對于JVM來說,新建一個線程的代價還是挺大的,決不同于新建一個對象
    • 資源消耗量。沒有一個池來限制線程的數(shù)量,會導(dǎo)致線程的數(shù)量直接取決于應(yīng)用的并發(fā)量,這樣有潛在的線程數(shù)據(jù)巨大的可能,那么資源消耗量將是巨大的
    • 穩(wěn)定性。當(dāng)線程數(shù)量超過系統(tǒng)資源所能承受的程度,穩(wěn)定性就會成問題

    制定執(zhí)行策略

    在每個需要多線程處理的地方,不管并發(fā)量有多大,需要考慮線程的執(zhí)行策略

    • 任務(wù)以什么順序執(zhí)行
    • 可以有多少個任何并發(fā)執(zhí)行
    • 可以有多少個任務(wù)進(jìn)入等待執(zhí)行隊列
    • 系統(tǒng)過載的時候,應(yīng)該放棄哪些任務(wù)?如何通知到應(yīng)用程序?
    • 一個任務(wù)的執(zhí)行前后應(yīng)該做什么處理

    線程池的類型

    不管是通過Executors創(chuàng)建線程池,還是通過Spring來管理,都得清楚知道有哪幾種線程池:

    • FixedThreadPool:定長線程池,提交任務(wù)時創(chuàng)建線程,直到池的最大容量,如果有線程非預(yù)期結(jié)束,會補(bǔ)充新線程
    • CachedThreadPool:可變線程池,它猶如一個彈簧,如果沒有任務(wù)需求時,它回收空閑線程,如果需求增加,則按需增加線程,不對池的大小做限制
    • SingleThreadExecutor:單線程。處理不過來的任務(wù)會進(jìn)入FIFO隊列等待執(zhí)行
    • SecheduledThreadPool:周期性線程池。支持執(zhí)行周期性線程任務(wù)

    其實,這些不同類型的線程池都是通過構(gòu)建一個ThreadPoolExecutor來完成的,所不同的是corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory這么幾個參數(shù)。具體可以參見JDK DOC。

    線程池飽和策略

    由以上線程池類型可知,除了CachedThreadPool其他線程池都有飽和的可能,當(dāng)飽和以后就需要相應(yīng)的策略處理請求線程的任務(wù),ThreadPoolExecutor采取的方式通過隊列來存儲這些任務(wù),當(dāng)然會根據(jù)池類型不同選擇不同的隊列,比如FixedThreadPool和SingleThreadExecutor默認(rèn)采用的是無限長度的LinkedBlockingQueue。但從系統(tǒng)可控性講,最好的做法是使用定長的ArrayBlockingQueue或有限的LinkedBlockingQueue,并且當(dāng)達(dá)到上限時通過ThreadPoolExecutor.setRejectedExecutionHandler方法設(shè)置一個拒絕任務(wù)的策略,JDK提供了AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy幾種策略,具體差異可見JDK DOC

    線程無依賴性

    多線程任務(wù)設(shè)計上盡量使得各任務(wù)是獨立無依賴的,所謂依賴性可兩個方面:

    • 線程之間的依賴性。如果線程有依賴可能會造成死鎖或饑餓
    • 調(diào)用者與線程的依賴性。調(diào)用者得監(jiān)視線程的完成情況,影響可并發(fā)量

    當(dāng)然,在有些業(yè)務(wù)里確實需要一定的依賴性,比如調(diào)用者需要得到線程完成后結(jié)果,傳統(tǒng)的Thread是不便完成的,因為run方法無返回值,只能通過一些共享的變量來傳遞結(jié)果,但在Executor框架里可以通過Future和Callable實現(xiàn)需要有返回值的任務(wù),當(dāng)然線程的異步性導(dǎo)致需要有相應(yīng)機(jī)制來保證調(diào)用者能等待任務(wù)完成,關(guān)于Future和Callable的用法見下面的實例就一目了然了:

     

    1. public class FutureRenderer {  
    2.     private final ExecutorService executor = ...;  
    3.     void renderPage(CharSequence source) {  
    4.         final List<ImageInfo> imageInfos = scanForImageInfo(source);  
    5.         Callable<List<ImageData>> task =  
    6.                 new Callable<List<ImageData>>() {  
    7.                     public List<ImageData> call() {  
    8.                         List<ImageData> result  
    9.                                 = new ArrayList<ImageData>();  
    10.                         for (ImageInfo imageInfo : imageInfos)  
    11.                             result.add(imageInfo.downloadImage());  
    12.                         return result;  
    13.                     }  
    14.                 };  
    15.         Future<List<ImageData>> future =  executor.submit(task);  
    16.         renderText(source);  
    17.         try {  
    18.             List<ImageData> imageData =  future.get();  
    19.             for (ImageData data : imageData)  
    20.                 renderImage(data);  
    21.         } catch (InterruptedException e) {  
    22.             // Re-assert the thread's interrupted status  
    23.             Thread.currentThread().interrupt();  
    24.             // We don't need the result, so cancel the task too  
    25.             future.cancel(true);  
    26.         } catch (ExecutionException e) {  
    27.             throw launderThrowable(e.getCause());  
    28.         }  
    29.     }  
    30. }  

     

    以上代碼關(guān)鍵在于List<ImageData> imageData = future.get();如果Callable類型的任務(wù)沒有執(zhí)行完時,調(diào)用者會阻塞等待。不過這樣的方式還是得謹(jǐn)慎使用,很容易造成不良設(shè)計。另外對于這種需要等待的場景,就需要設(shè)置一個最大容忍時間timeout,設(shè)置方法可以在future.get()加上timeout參數(shù),或是再調(diào)用ExecutorService.invokeAll 加上timeout參數(shù)

    線程的取消與關(guān)閉 

    一般的情況下是讓線程運行完成后自行關(guān)閉,但有些時候也會中途取消或關(guān)閉線程,比如以下情況:

    • 調(diào)用者強(qiáng)制取消。比如一個長時間運行的任務(wù),用戶點擊"cancel"按鈕強(qiáng)行取消
    • 限時任務(wù)
    • 發(fā)生不可處理的任務(wù)
    • 整個應(yīng)用程序或服務(wù)的關(guān)閉

    因此需要有相應(yīng)的取消或關(guān)閉的方法和策略來控制線程,一般有以下方法:

    1)通過變量標(biāo)識來控制

    這種方式比較老土,但使用得非常廣泛,主要缺點是對有阻塞的操作控制不好,代碼示例如下所示:

     

    1. public class PrimeGenerator implements Runnable {  
    2.      @GuardedBy("this")  
    3.      private final List<BigInteger> primes  
    4.              = new ArrayList<BigInteger>();  
    5.      private  volatile boolean cancelled;  
    6.      public void run() {  
    7.          BigInteger p = BigInteger.ONE;  
    8.          while (!cancelled ) {  
    9.              p = p.nextProbablePrime();  
    10.              synchronized (this) {  
    11.                  primes.add(p);  
    12.              }  
    13.          }  
    14.      }  
    15.      public void cancel() { cancelled = true;  }  
    16.      public synchronized List<BigInteger> get() {  
    17.          return new ArrayList<BigInteger>(primes);  
    18.      }  
    19. }  

     

    2)中斷

    中斷通常是實現(xiàn)取消最明智的選擇,但線程自身需要支持中斷處理,并且要處理好中斷策略,一般響應(yīng)中斷的方式有兩種:

    • 處理完中斷清理后繼續(xù)傳遞中斷異常(InterruptedException)
    • 調(diào)用interrupt方法,使得上層能感知到中斷異常

    3) 取消不可中斷阻塞

    存在一些不可中斷的阻塞,比如:

    • java.io和java.nio中同步讀寫IO
    • Selector的異步IO
    • 獲取鎖

    對于這些線程的取消,則需要特定情況特定對待,比如對于socket阻塞,如果要安全取消,則需要調(diào)用socket.close()

    4)JVM的關(guān)閉

    如果有任務(wù)需要在JVM關(guān)閉之前做一些清理工作,而不是被JVM強(qiáng)硬關(guān)閉掉,可以使用JVM的鉤子技術(shù),其實JVM鉤子也只是個很普通的技術(shù),也就是用個map把一些需要JVM關(guān)閉前啟動的任務(wù)保存下來,在JVM關(guān)閉過程中的某個環(huán)節(jié)來并發(fā)啟動這些任務(wù)線程。具體使用示例如下:

     

    1. public void start() {  
    2.     Runtime.getRuntime().addShutdownHook(new Thread() {  
    3.         public void run() {  
    4.             try { LogService.this.stop(); }  
    5.             catch (InterruptedException ignored) {}  
    6.         }  
    7.     });  
    8. }  

     

    posted on 2011-10-13 16:04 kxbin 閱讀(351) 評論(0)  編輯  收藏 所屬分類: java基礎(chǔ)
    你恨一個人是因為你愛他;你喜歡一個人,是因為他身上有你沒有的;你討厭一個人是因為他身上有你有的東西;你經(jīng)常在別人面前批評某人,其實潛意識中是想接近他。

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(5)

    隨筆檔案

    文章分類

    文章檔案

    相冊

    收藏夾

    J2EE

    java技術(shù)網(wǎng)站

    Linux

    平時常去的網(wǎng)站

    數(shù)據(jù)庫

    電影網(wǎng)站

    網(wǎng)站設(shè)計

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 我想看一级毛片免费的| 国产精品黄页在线播放免费| 国产午夜免费秋霞影院| 黑人大战亚洲人精品一区| 亚洲成a人片在线观看中文!!!| 美女被暴羞羞免费视频| 午夜免费福利小电影| 国产无遮挡色视频免费视频| 亚洲va国产va天堂va久久| 亚洲人成无码网站在线观看| 免费久久人人爽人人爽av| 四虎www免费人成| 久久青青草原亚洲AV无码麻豆 | 亚洲人成免费电影| 一级毛片免费观看不收费| 2021免费日韩视频网| 亚洲小说区图片区另类春色| 亚洲国产视频久久| 免费黄色电影在线观看| 又粗又硬免费毛片| 亚洲伊人久久大香线蕉啊| 91在线免费视频| 国产精品成人无码免费| 亚洲欧洲日本精品| 91免费在线视频| 免费看男女下面日出水视频| 亚洲mv国产精品mv日本mv| 成人影片一区免费观看| 人人狠狠综合久久亚洲高清| 亚洲人成网男女大片在线播放 | 91手机看片国产永久免费| 亚洲线精品一区二区三区| 精品国产日韩亚洲一区在线| 97在线观看永久免费视频| 亚洲A∨无码无在线观看| 黄色网址免费在线观看| 国产裸模视频免费区无码| 国产精品亚洲午夜一区二区三区| 一个人免费日韩不卡视频| 亚洲免费观看视频| 一级毛片大全免费播放下载 |