這篇文章將為大家詳細講解有關Java線程異常結束的解決方法,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
Java中線程異常結束的解決方法是:解決線程異常結束的關鍵是要捕獲線程執行過程中所產生的異常,當線程異常時,會通過調用setUncaughtExceptionHandler方法來捕獲異常再解決
【】
我們開發工程中經常使用到線程,在線程使用上,我們可能會有這樣的場景:
(1)伴隨這一個業務產生一個比較耗時的任務,而這個業務返回并不需要等待該任務。那我們往往會啟動一個線程去完成這個異步任務。
(2)我們需要一個定時任務比如:定時清除數據,我們會起一個定時執行線程去做該任務。
上述問題比較簡單,new一個線程然后去做這件事。但是我們常常忽略一個問題,線程異常了怎么辦?比如耗時任務我們只完成了一半,我們就異常結束了(這里不考慮事務一致性,我們只考慮一定要將任務完成)。又比如在清數據的時候,數據庫發生斷連。這時候我們會發現線程死掉了,任務終止了,我們需要重啟整個項目把該定時任務起起來。
解決這些問題的關鍵就是,如何捕獲線程執行過程中產生的異常?
我們查看JDK API我們會發現在Thread中有setUncaughtExceptionHandler方法,讓我們可以在線程發生異常時,調用該方法。
解決方法:
場景一解決思路:
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); //注釋調 否則看不到輸出 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+"--"+"啟動"); while(task>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(System.currentTimeMillis()%3==0){ throw new RuntimeException("模擬異常"); } System.out.println(threadName+"--"+"執行task"+task); task--; } System.out.println(threadName+"--"+"正常終止"); } } }
結果輸出:
Thread-0--啟動 Thread-0--執行task10 Thread-0--執行task9 Thread-0--執行task8 Thread-0--執行task7 模擬異常 Thread-1--啟動 Thread-1--執行task6 Thread-1--執行task5 模擬異常 Thread-2--啟動 Thread-2--執行task4 Thread-2--執行task3 模擬異常 Thread-3--啟動 Thread-3--執行task2 模擬異常 Thread-4--啟動 Thread-4--執行task1 Thread-4--正常終止
還是場景一我們來看一下線程池的方式,思路是一樣的為什么要再寫一個單線程的線程池方式呢?
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+"--"+"啟動"); while(task>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(System.currentTimeMillis()%3==0){ throw new RuntimeException("模擬異常"); } System.out.println(threadName+"--"+"執行task"+task); task--; } System.out.println(threadName+"--"+"正常終止"); } } }
結果輸出:
Thread-0--啟動 Thread-0--執行task10 Thread-0--執行task9 Thread-1--啟動 Thread-1--執行task8 Thread-2--啟動 Thread-2--執行task7 Thread-2--執行task6 Thread-2--執行task5 Thread-2--執行task4 Thread-2--執行task3 Thread-2--執行task2 Thread-3--啟動 Thread-3--執行task1 Thread-3--正常終止
由于這邊只是用單線程,所以發現和上面區別不大。不過也展示了線程池是如何捕獲線程異常的。
場景二解決方法
現在我們看看場景二定時任務,為什么我要寫一份單線程池的捕獲異常方式,就是用于和下面做對比。
定時任務我們常常用ScheduledExecutorService,和上述ExecutorService獲取方式一樣。但是如果我們參照上述方式寫定時任務,然后獲取異常。我們會發現我們無法在uncaughtException方法內獲取到線程的異常。異常消失了,或者說線程發生異常根本就沒調用uncaughtException方法。后來查看相關API,發現在ScheduledExecutorService獲取異常的方式可以使用ScheduledFuture對象來獲取具體方式如下:
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+"--"+"啟動"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(System.currentTimeMillis()%3==0){ throw new RuntimeException("模擬異常"); } System.out.println(threadName+"--"+"執行task"+count); count++; System.out.println(threadName+"--"+"正常終止"); } } }
結果輸出:
pool-1-thread-1--啟動 java.lang.RuntimeException: 模擬異常 pool-2-thread-1--啟動 pool-2-thread-1--執行task0 pool-2-thread-1--正常終止 pool-2-thread-1--啟動 pool-2-thread-1--執行task1 pool-2-thread-1--正常終止 pool-2-thread-1--啟動 pool-2-thread-1--執行task2 pool-2-thread-1--正常終止 pool-2-thread-1--啟動 java.lang.RuntimeException: 模擬異常 pool-3-thread-1--啟動 pool-3-thread-1--執行task3 pool-3-thread-1--正常終止 pool-3-thread-1--啟動 java.lang.RuntimeException: 模擬異常 pool-4-thread-1--啟動 pool-4-thread-1--執行task4 pool-4-thread-1--正常終止 .....
至此我們實現了就算定時任務發生異常,總有一個線程會去執行。一個線程倒下,會有后續線程補上。
關于Java線程異常結束的解決方法就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。