http://sunnylocus.iteye.com/blog/538282當(dāng)一個單線程化的控制臺程序因為未捕獲的異常終止的時候,程序停止運行,并生了棧追蹤,這與典型的程序輸出不同,當(dāng)一個程序發(fā)生了異常說明有不穩(wěn)定的因素存在。如果并發(fā)程序中線程失敗就沒那么容易發(fā)現(xiàn)了。棧追蹤可能會從控制臺輸出,但是沒有人會去一直在看控制臺,并且,當(dāng)線程失敗的時候,應(yīng)用程序可能看起來仍在工作。就象程序能跑在50個線程池上,也能夠跑在49個線程的線程池上,區(qū)別在于50個人干的活要比49個人干的活要多的多。
導(dǎo)致線程死亡的的最主要的原因是RuntimeException。因為這些異常表明一個程序錯誤或者不可修復(fù)的錯誤,它們不著順著棧的調(diào)用傳遞,此時,默認(rèn)的行為是在控制臺打印棧追蹤的信息,并終止線程。
我們舉個例子,將奴隸主比作是你寫的程序,奴隸比作是線程。假如你是奴隸主,你手下有5名奴隸,你分派他們一項任務(wù)去將不斷開采來的石頭搬到某個地方。如果中途有奴隸逃跑,那么搬運石頭的效率就會下降,如果你沒有措施發(fā)現(xiàn)奴隸逃跑,最后連一個奴隸也沒有了,沒有人再去搬石頭了。當(dāng)你發(fā)現(xiàn)程序有問題,比如程序剛啟動的時候處理速度很快,以后越跑越慢,最后完全停止了,你可能不知道問題出在哪兒,其實這都是因為線程泄露引起的。想解決奴隸逃跑問題,不難,給每個奴隸戴上個報警器,一逃跑報警器就給你發(fā)信息,告訴哪個奴隸,因為什么原因逃跑了,可以根據(jù)需要再增加一名奴隸,讓搬石頭的奴隸數(shù)量始終維持在5名或著將信息記錄到文件,便于分析導(dǎo)致線程泄露的原意改進程序。ok,我們用代碼說話
1、定義好報警器
- package com.bill99.thread.test;
-
- import java.lang.Thread.UncaughtExceptionHandler;
-
-
- public class UEHLogger implements UncaughtExceptionHandler {
- public void uncaughtException(Thread t, Throwable e) {
- System.out.println(String.format("不好了,有奴隸逃跑了!奴隸姓名:%1$s,編號:%2$s,逃跑原因:%3$s", t.getName(),t.getId(),e.getMessage()));
- System.out.println("還剩"+HelotPool.helotPool.getActiveCount()+"個奴隸");
- }
- }
package com.bill99.thread.test;
import java.lang.Thread.UncaughtExceptionHandler;
//將泄露的線程信息輸出到控制臺
public class UEHLogger implements UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println(String.format("不好了,有奴隸逃跑了!奴隸姓名:%1$s,編號:%2$s,逃跑原因:%3$s", t.getName(),t.getId(),e.getMessage()));
System.out.println("還剩"+HelotPool.helotPool.getActiveCount()+"個奴隸");
}
}
2、我們先建立一個奴隸工廠,每名奴隸出工廠的時候都會有一個報警器
- package com.bill99.thread.test;
-
- import java.util.concurrent.ThreadFactory;
-
- public class HelotFactory implements ThreadFactory {
- private volatile int helotId=0;
-
- public Thread newThread(Runnable r) {
- Thread helotThread = new Thread(r);
- helotThread.setName("helot-Thread-"+gethelotId());
- helotThread.setUncaughtExceptionHandler(new UEHLogger());
- return helotThread;
- }
- private int gethelotId(){
- return ++helotId;
- }
- }
package com.bill99.thread.test;
import java.util.concurrent.ThreadFactory;
//奴隸制造工廠
public class HelotFactory implements ThreadFactory {
private volatile int helotId=0;//奴隸編號
//產(chǎn)生一個新奴隸
public Thread newThread(Runnable r) {
Thread helotThread = new Thread(r);
helotThread.setName("helot-Thread-"+gethelotId());//設(shè)置奴隸姓名
helotThread.setUncaughtExceptionHandler(new UEHLogger());//UEHLogger就是報警器
return helotThread;
}
private int gethelotId(){
return ++helotId;
}
}
3、奴隸逃跑測試,看看是否會觸發(fā)報警器
- package com.bill99.thread.test;
-
- import java.util.concurrent.ThreadFactory;
-
- public class HelotEscapeTest {
- private ThreadFactory factory = new HelotFactory();
- private Thread helotThread = null;
-
- public HelotEscapeTest(){
- Runnable task = new Runnable() {
- int stoneNum=1;
- public void run() {
- while(!Thread.interrupted()){
- System.out.println(helotThread.getName()+" 搬第"+stoneNum+"塊石頭..");
- stoneNum++;
- try{
- Thread.sleep(500);
- } catch(InterruptedException e){e.printStackTrace();}
- if(stoneNum>100){
- throw new RuntimeException("又餓又累沒力氣搬石頭了");
- }
- }
- }
- };
- helotThread = factory.newThread(task);
- }
-
- public void startWork(){
- helotThread.start();
- }
- public static void main(String[] args) {
- HelotEscapeTest test = new HelotEscapeTest();
- test.startWork();
- }
- }
package com.bill99.thread.test;
import java.util.concurrent.ThreadFactory;
//奴隸逃跑測試
public class HelotEscapeTest {
private ThreadFactory factory = new HelotFactory();
private Thread helotThread = null;
public HelotEscapeTest(){
Runnable task = new Runnable() {
int stoneNum=1;
public void run() {
while(!Thread.interrupted()){
System.out.println(helotThread.getName()+" 搬第"+stoneNum+"塊石頭..");
stoneNum++;
try{
Thread.sleep(500);
} catch(InterruptedException e){e.printStackTrace();}
if(stoneNum>100){
throw new RuntimeException("又餓又累沒力氣搬石頭了");
}
}
}
};
helotThread = factory.newThread(task);
}
//開始干活
public void startWork(){
helotThread.start();
}
public static void main(String[] args) {
HelotEscapeTest test = new HelotEscapeTest();
test.startWork();
}
}
運行程序后,奴隸在搬完100塊石頭后不干了,報警器給出提示信息
不好了,有奴隸逃跑了!奴隸姓名:helot-Thread-1,編號:7,逃跑原因:又餓又累沒力氣搬石頭了
這樣我就能找出線程泄露的原因了。如果在線程池中發(fā)生了泄露是否也能記錄?
4、奴隸池測試
- package com.bill99.thread.test;
-
- import java.util.Random;
- import java.util.concurrent.BlockingQueue;
- import java.util.concurrent.LinkedBlockingQueue;
- import java.util.concurrent.ThreadFactory;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
-
- public class HelotPoolTest {
-
- public static ThreadPoolExecutor helotPool;
- private int helotNum= 5;
- private Random random = new Random(100);
-
- private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
-
- public HelotPoolTest() {
- ThreadFactory factory = new HelotFactory();
- helotPool = new ThreadPoolExecutor( helotNum,helotNum,1000,TimeUnit.SECONDS,queue,factory);
- }
-
- public void assignTask(){
- final int MAX=100;
- final int MIN=10;
- Runnable task = null;
- for(int j=0;j<20;j++){
- task = new Runnable() {
- int stoneNum = random.nextInt(MAX - MIN + 1) + MIN;
- public void run() {
- for (int i = 1; i <= stoneNum; i++) {
- System.out.println(Thread.currentThread().getName() + " 搬完"+ i + "塊石頭");
- if (i == 60) {
- throw new RuntimeException("搬完第60塊石頭不干了");
- }
- try{
- Thread.sleep(100);
- }catch(InterruptedException e){e.printStackTrace();}
- }
- }
- };
- queue.add(task);
- }
- }
-
- public void startWork(){
- helotPool.prestartAllCoreThreads();
- }
- public static void main(String[] args) {
- HelotPoolTest pool = new HelotPoolTest();
- pool.assignTask();
- pool.startWork();
- }
- }
package com.bill99.thread.test;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class HelotPoolTest {
public static ThreadPoolExecutor helotPool;
private int helotNum= 5; //奴隸數(shù)
private Random random = new Random(100);
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
public HelotPoolTest() {
ThreadFactory factory = new HelotFactory();
helotPool = new ThreadPoolExecutor( helotNum,helotNum,1000,TimeUnit.SECONDS,queue,factory);
}
//分配任務(wù)
public void assignTask(){
final int MAX=100;
final int MIN=10;
Runnable task = null;
for(int j=0;j<20;j++){
task = new Runnable() {
int stoneNum = random.nextInt(MAX - MIN + 1) + MIN;// 生成10~100范圍的隨機數(shù)
public void run() {
for (int i = 1; i <= stoneNum; i++) {
System.out.println(Thread.currentThread().getName() + " 搬完"+ i + "塊石頭");
if (i == 60) {
throw new RuntimeException("搬完第60塊石頭不干了");
}
try{
Thread.sleep(100);//休息下
}catch(InterruptedException e){e.printStackTrace();}
}
}
};
queue.add(task);
}
}
//開始干活
public void startWork(){
helotPool.prestartAllCoreThreads();
}
public static void main(String[] args) {
HelotPoolTest pool = new HelotPoolTest();
pool.assignTask();
pool.startWork();
}
}
程序啟動,用Jprofiler監(jiān)控,剛啟動時會有5名奴隸干活不一會就會有線程退出,最后5個線程全部退,報警器對每個線程退出都能記錄到導(dǎo)致線程退出的原因。標(biāo)準(zhǔn)的Executor實現(xiàn)是:
在需求不高時回收空閑的線程,在需求增加時添加新的線程,如果任務(wù)拋出了異常,就會用一個全新的工作線程取代出錯的那個。
JDK文檔是這么說的,不過通過Jprofiler監(jiān)控只有在5名奴隸全部逃跑,沒人干活的時候ThreadPoolExecutor才會生成一個新線程繼續(xù)搬石頭,并不是只要一個線程退出就會馬上生成新線程去代替。
Author: orangelizq
email: orangelizq@163.com
posted on 2013-05-29 16:43
桔子汁 閱讀(600)
評論(0) 編輯 收藏 所屬分類:
J2SE