您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Java線程異常結(jié)束的解決方法,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
Java中線程異常結(jié)束的解決方法是:解決線程異常結(jié)束的關(guān)鍵是要捕獲線程執(zhí)行過(guò)程中所產(chǎn)生的異常,當(dāng)線程異常時(shí),會(huì)通過(guò)調(diào)用setUncaughtExceptionHandler方法來(lái)捕獲異常再解決
【】
我們開發(fā)工程中經(jīng)常使用到線程,在線程使用上,我們可能會(huì)有這樣的場(chǎng)景:
(1)伴隨這一個(gè)業(yè)務(wù)產(chǎn)生一個(gè)比較耗時(shí)的任務(wù),而這個(gè)業(yè)務(wù)返回并不需要等待該任務(wù)。那我們往往會(huì)啟動(dòng)一個(gè)線程去完成這個(gè)異步任務(wù)。
(2)我們需要一個(gè)定時(shí)任務(wù)比如:定時(shí)清除數(shù)據(jù),我們會(huì)起一個(gè)定時(shí)執(zhí)行線程去做該任務(wù)。
上述問(wèn)題比較簡(jiǎn)單,new一個(gè)線程然后去做這件事。但是我們常常忽略一個(gè)問(wèn)題,線程異常了怎么辦?比如耗時(shí)任務(wù)我們只完成了一半,我們就異常結(jié)束了(這里不考慮事務(wù)一致性,我們只考慮一定要將任務(wù)完成)。又比如在清數(shù)據(jù)的時(shí)候,數(shù)據(jù)庫(kù)發(fā)生斷連。這時(shí)候我們會(huì)發(fā)現(xiàn)線程死掉了,任務(wù)終止了,我們需要重啟整個(gè)項(xiàng)目把該定時(shí)任務(wù)起起來(lái)。
解決這些問(wèn)題的關(guān)鍵就是,如何捕獲線程執(zhí)行過(guò)程中產(chǎn)生的異常?
我們查看JDK API我們會(huì)發(fā)現(xiàn)在Thread中有setUncaughtExceptionHandler方法,讓我們可以在線程發(fā)生異常時(shí),調(diào)用該方法。
解決方法:
場(chǎng)景一解決思路:
public class Plan1 { private SimpleTask task = new SimpleTask(); public static void main(String[] args) { Plan1 plan = new Plan1(); plan.start(); } public void start(){ Thread thread = new Thread(task); //thread.setDaemon(true); //注釋調(diào) 否則看不到輸出 thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){ @Override public void uncaughtException(Thread t, Throwable e) { System.out.println(e.getMessage()); start(); } }); thread.start(); } class SimpleTask implements Runnable{ private int task = 10; @Override public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName+"--"+"啟動(dòng)"); while(task>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(System.currentTimeMillis()%3==0){ throw new RuntimeException("模擬異常"); } System.out.println(threadName+"--"+"執(zhí)行task"+task); task--; } System.out.println(threadName+"--"+"正常終止"); } } }
結(jié)果輸出:
Thread-0--啟動(dòng) Thread-0--執(zhí)行task10 Thread-0--執(zhí)行task9 Thread-0--執(zhí)行task8 Thread-0--執(zhí)行task7 模擬異常 Thread-1--啟動(dòng) Thread-1--執(zhí)行task6 Thread-1--執(zhí)行task5 模擬異常 Thread-2--啟動(dòng) Thread-2--執(zhí)行task4 Thread-2--執(zhí)行task3 模擬異常 Thread-3--啟動(dòng) Thread-3--執(zhí)行task2 模擬異常 Thread-4--啟動(dòng) Thread-4--執(zhí)行task1 Thread-4--正常終止
還是場(chǎng)景一我們來(lái)看一下線程池的方式,思路是一樣的為什么要再寫一個(gè)單線程的線程池方式呢?
public class Plan3 { private SimpleTask task = new SimpleTask(); private MyFactory factory = new MyFactory(task); public static void main(String[] args) { Plan3 plan = new Plan3(); ExecutorService pool = Executors.newSingleThreadExecutor(plan.factory); pool.execute(plan.task); pool.shutdown(); } class MyFactory implements ThreadFactory{ private SimpleTask task; public MyFactory(SimpleTask task) { super(); this.task = task; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { ExecutorService pool = Executors.newSingleThreadExecutor(new MyFactory(task)); pool.execute(task); pool.shutdown(); } }); return thread; } } class SimpleTask implements Runnable{ private int task = 10; @Override public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName+"--"+"啟動(dòng)"); while(task>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(System.currentTimeMillis()%3==0){ throw new RuntimeException("模擬異常"); } System.out.println(threadName+"--"+"執(zhí)行task"+task); task--; } System.out.println(threadName+"--"+"正常終止"); } } }
結(jié)果輸出:
Thread-0--啟動(dòng) Thread-0--執(zhí)行task10 Thread-0--執(zhí)行task9 Thread-1--啟動(dòng) Thread-1--執(zhí)行task8 Thread-2--啟動(dòng) Thread-2--執(zhí)行task7 Thread-2--執(zhí)行task6 Thread-2--執(zhí)行task5 Thread-2--執(zhí)行task4 Thread-2--執(zhí)行task3 Thread-2--執(zhí)行task2 Thread-3--啟動(dòng) Thread-3--執(zhí)行task1 Thread-3--正常終止
由于這邊只是用單線程,所以發(fā)現(xiàn)和上面區(qū)別不大。不過(guò)也展示了線程池是如何捕獲線程異常的。
場(chǎng)景二解決方法
現(xiàn)在我們看看場(chǎng)景二定時(shí)任務(wù),為什么我要寫一份單線程池的捕獲異常方式,就是用于和下面做對(duì)比。
定時(shí)任務(wù)我們常常用ScheduledExecutorService,和上述ExecutorService獲取方式一樣。但是如果我們參照上述方式寫定時(shí)任務(wù),然后獲取異常。我們會(huì)發(fā)現(xiàn)我們無(wú)法在uncaughtException方法內(nèi)獲取到線程的異常。異常消失了,或者說(shuō)線程發(fā)生異常根本就沒(méi)調(diào)用uncaughtException方法。后來(lái)查看相關(guān)API,發(fā)現(xiàn)在ScheduledExecutorService獲取異常的方式可以使用ScheduledFuture對(duì)象來(lái)獲取具體方式如下:
public class Plan2 { private SimpleTask task = new SimpleTask(); public static void main(String[] args) { Plan2 plan = new Plan2(); start(plan.task); } public static void start(SimpleTask task){ ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture<?> future = pool.scheduleAtFixedRate(task, 0, 1000, TimeUnit.MILLISECONDS); try { future.get(); } catch (InterruptedException | ExecutionException e) { System.out.println(e.getMessage()); start(task); }finally { pool.shutdown(); } } class SimpleTask implements Runnable{ private volatile int count = 0; @Override public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName+"--"+"啟動(dòng)"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(System.currentTimeMillis()%3==0){ throw new RuntimeException("模擬異常"); } System.out.println(threadName+"--"+"執(zhí)行task"+count); count++; System.out.println(threadName+"--"+"正常終止"); } } }
結(jié)果輸出:
pool-1-thread-1--啟動(dòng) java.lang.RuntimeException: 模擬異常 pool-2-thread-1--啟動(dòng) pool-2-thread-1--執(zhí)行task0 pool-2-thread-1--正常終止 pool-2-thread-1--啟動(dòng) pool-2-thread-1--執(zhí)行task1 pool-2-thread-1--正常終止 pool-2-thread-1--啟動(dòng) pool-2-thread-1--執(zhí)行task2 pool-2-thread-1--正常終止 pool-2-thread-1--啟動(dòng) java.lang.RuntimeException: 模擬異常 pool-3-thread-1--啟動(dòng) pool-3-thread-1--執(zhí)行task3 pool-3-thread-1--正常終止 pool-3-thread-1--啟動(dòng) java.lang.RuntimeException: 模擬異常 pool-4-thread-1--啟動(dòng) pool-4-thread-1--執(zhí)行task4 pool-4-thread-1--正常終止 .....
至此我們實(shí)現(xiàn)了就算定時(shí)任務(wù)發(fā)生異常,總有一個(gè)線程會(huì)去執(zhí)行。一個(gè)線程倒下,會(huì)有后續(xù)線程補(bǔ)上。
關(guān)于Java線程異常結(jié)束的解決方法就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。