隨筆-128  評論-55  文章-5  trackbacks-0

          http://sunnylocus.iteye.com/blog/538282

          當一個單線程化的控制臺程序因為未捕獲的異常終止的時候,程序停止運行,并生了棧追蹤,這與典型的程序輸出不同,當一個程序發生了異常說明有不穩定的因素存在。如果并發程序中線程失敗就沒那么容易發現了。棧追蹤可能會從控制臺輸出,但是沒有人會去一直在看控制臺,并且,當線程失敗的時候,應用程序可能看起來仍在工作。就象程序能跑在50個線程池上,也能夠跑在49個線程的線程池上,區別在于50個人干的活要比49個人干的活要多的多。

            導致線程死亡的的最主要的原因是RuntimeException。因為這些異常表明一個程序錯誤或者不可修復的錯誤,它們不著順著棧的調用傳遞,此時,默認的行為是在控制臺打印棧追蹤的信息,并終止線程。

            我們舉個例子,將奴隸主比作是你寫的程序,奴隸比作是線程。假如你是奴隸主,你手下有5名奴隸,你分派他們一項任務去將不斷開采來的石頭搬到某個地方。如果中途有奴隸逃跑,那么搬運石頭的效率就會下降,如果你沒有措施發現奴隸逃跑,最后連一個奴隸也沒有了,沒有人再去搬石頭了。當你發現程序有問題,比如程序剛啟動的時候處理速度很快,以后越跑越慢,最后完全停止了,你可能不知道問題出在哪兒,其實這都是因為線程泄露引起的。想解決奴隸逃跑問題,不難,給每個奴隸戴上個報警器,一逃跑報警器就給你發信息,告訴哪個奴隸,因為什么原因逃跑了,可以根據需要再增加一名奴隸,讓搬石頭的奴隸數量始終維持在5名或著將信息記錄到文件,便于分析導致線程泄露的原意改進程序。ok,我們用代碼說話

          1、定義好報警器

          Java代碼 復制代碼 收藏代碼
          1. package com.bill99.thread.test;   
          2.   
          3. import java.lang.Thread.UncaughtExceptionHandler;   
          4.   
          5. //將泄露的線程信息輸出到控制臺   
          6. public class UEHLogger implements UncaughtExceptionHandler {   
          7.     public void uncaughtException(Thread t, Throwable e) {   
          8.         System.out.println(String.format("不好了,有奴隸逃跑了!奴隸姓名:%1$s,編號:%2$s,逃跑原因:%3$s", t.getName(),t.getId(),e.getMessage()));   
          9.         System.out.println("還剩"+HelotPool.helotPool.getActiveCount()+"個奴隸");   
          10.     }   
          11. }  

            

          2、我們先建立一個奴隸工廠,每名奴隸出工廠的時候都會有一個報警器

              

          Java代碼 復制代碼 收藏代碼
          1. package com.bill99.thread.test;   
          2.   
          3. import java.util.concurrent.ThreadFactory;   
          4. //奴隸制造工廠   
          5. public class HelotFactory implements ThreadFactory {   
          6.     private volatile int helotId=0;//奴隸編號   
          7.     //產生一個新奴隸   
          8.     public Thread newThread(Runnable r) {   
          9.         Thread helotThread = new Thread(r);   
          10.         helotThread.setName("helot-Thread-"+gethelotId());//設置奴隸姓名   
          11.         helotThread.setUncaughtExceptionHandler(new UEHLogger());//UEHLogger就是報警器   
          12.         return helotThread;   
          13.     }   
          14.     private int gethelotId(){   
          15.         return ++helotId;   
          16.     }   
          17. }  

           3、奴隸逃跑測試,看看是否會觸發報警器

          Java代碼 復制代碼 收藏代碼
          1. package com.bill99.thread.test;   
          2.   
          3. import java.util.concurrent.ThreadFactory;   
          4. //奴隸逃跑測試   
          5. public class HelotEscapeTest {   
          6.     private ThreadFactory factory = new HelotFactory();   
          7.     private Thread helotThread = null;   
          8.        
          9.     public HelotEscapeTest(){   
          10.         Runnable task = new Runnable() {   
          11.             int stoneNum=1;   
          12.             public void run() {   
          13.                 while(!Thread.interrupted()){   
          14.                     System.out.println(helotThread.getName()+" 搬第"+stoneNum+"塊石頭..");   
          15.                     stoneNum++;   
          16.                     try{   
          17.                         Thread.sleep(500);   
          18.                     } catch(InterruptedException e){e.printStackTrace();}   
          19.                     if(stoneNum>100){   
          20.                         throw new RuntimeException("又餓又累沒力氣搬石頭了");   
          21.                     }   
          22.                 }   
          23.             }   
          24.         };   
          25.         helotThread = factory.newThread(task);   
          26.     }   
          27.     //開始干活   
          28.     public void startWork(){   
          29.         helotThread.start();   
          30.     }   
          31.     public static void main(String[] args) {   
          32.         HelotEscapeTest test = new HelotEscapeTest();   
          33.         test.startWork();   
          34.     }   
          35. }  

           運行程序后,奴隸在搬完100塊石頭后不干了,報警器給出提示信息

             不好了,有奴隸逃跑了!奴隸姓名:helot-Thread-1,編號:7,逃跑原因:又餓又累沒力氣搬石頭了

          這樣我就能找出線程泄露的原因了。如果在線程池中發生了泄露是否也能記錄?

          4、奴隸池測試

          Java代碼 復制代碼 收藏代碼
          1. package com.bill99.thread.test;   
          2.   
          3. import java.util.Random;   
          4. import java.util.concurrent.BlockingQueue;   
          5. import java.util.concurrent.LinkedBlockingQueue;   
          6. import java.util.concurrent.ThreadFactory;   
          7. import java.util.concurrent.ThreadPoolExecutor;   
          8. import java.util.concurrent.TimeUnit;   
          9.   
          10. public class HelotPoolTest {   
          11.        
          12.     public static ThreadPoolExecutor helotPool;   
          13.     private int helotNum= 5;  //奴隸數   
          14.     private Random random = new Random(100);   
          15.        
          16.     private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();   
          17.        
          18.     public HelotPoolTest() {   
          19.         ThreadFactory factory = new HelotFactory();   
          20.         helotPool = new ThreadPoolExecutor( helotNum,helotNum,1000,TimeUnit.SECONDS,queue,factory);   
          21.     }   
          22.     //分配任務   
          23.     public void assignTask(){   
          24.         final int MAX=100;   
          25.         final int MIN=10;   
          26.         Runnable task = null;   
          27.         for(int j=0;j<20;j++){   
          28.             task = new Runnable() {   
          29.                 int stoneNum = random.nextInt(MAX - MIN + 1) + MIN;// 生成10~100范圍的隨機數   
          30.                 public void run() {   
          31.                     for (int i = 1; i <= stoneNum; i++) {   
          32.                         System.out.println(Thread.currentThread().getName() + " 搬完"+ i + "塊石頭");   
          33.                         if (i == 60) {   
          34.                             throw new RuntimeException("搬完第60塊石頭不干了");   
          35.                         }   
          36.                         try{   
          37.                             Thread.sleep(100);//休息下   
          38.                         }catch(InterruptedException e){e.printStackTrace();}   
          39.                     }   
          40.                 }   
          41.             };   
          42.             queue.add(task);   
          43.         }   
          44.     }   
          45.     //開始干活   
          46.     public void startWork(){   
          47.         helotPool.prestartAllCoreThreads();   
          48.     }   
          49.     public static void main(String[] args) {   
          50.         HelotPoolTest pool = new HelotPoolTest();   
          51.         pool.assignTask();   
          52.         pool.startWork();   
          53.     }   
          54. }  

           程序啟動,用Jprofiler監控,剛啟動時會有5名奴隸干活不一會就會有線程退出,最后5個線程全部退,報警器對每個線程退出都能記錄到導致線程退出的原因。標準的Executor實現是:

          在需求不高時回收空閑的線程,在需求增加時添加新的線程,如果任務拋出了異常,就會用一個全新的工作線程取代出錯的那個。

          JDK文檔是這么說的,不過通過Jprofiler監控只有在5名奴隸全部逃跑,沒人干活的時候ThreadPoolExecutor才會生成一個新線程繼續搬石頭,并不是只要一個線程退出就會馬上生成新線程去代替。



          Author: orangelizq
          email: orangelizq@163.com

          歡迎大家訪問我的個人網站 萌萌的IT人
          posted on 2013-05-29 16:43 桔子汁 閱讀(601) 評論(0)  編輯  收藏 所屬分類: J2SE
          主站蜘蛛池模板: 壤塘县| 浦江县| 广德县| 安图县| 富阳市| 凉城县| 莱西市| 连江县| 宽城| 丁青县| 贵港市| 宁海县| 乌鲁木齐市| 达拉特旗| 宁夏| 岗巴县| 铁岭市| 嘉黎县| 汝州市| 永兴县| 略阳县| 龙井市| 宜春市| 博白县| 和平区| 土默特右旗| 甘洛县| 富源县| 甘肃省| 六枝特区| 清涧县| 凤山县| 崇信县| 卓尼县| 贺州市| 托克托县| 色达县| 郧西县| 大悟县| 德惠市| 容城县|