溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java線程異常結束的解決方法

發布時間:2020-09-08 11:39:56 來源:億速云 閱讀:418 作者:小新 欄目:編程語言

這篇文章將為大家詳細講解有關Java線程異常結束的解決方法,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

Java中線程異常結束的解決方法是:解決線程異常結束的關鍵是要捕獲線程執行過程中所產生的異常,當線程異常時,會通過調用setUncaughtExceptionHandler方法來捕獲異常再解決

Java線程異常結束的解決方法

我們開發工程中經常使用到線程,在線程使用上,我們可能會有這樣的場景:

(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線程異常結束的解決方法就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女