首頁 > Java > java教程 > 主體

Java執行緒異常結束怎麼解決

清浅
發布: 2020-10-13 09:45:53
原創
5782 人瀏覽過

Java中執行緒異常結束的解決方法是:先捕捉在執行緒執行過程中所產生的例外;然後當執行緒異常時,會透過呼叫setUncaughtExceptionHandler方法來捕獲異常;最後再解決即可。

Java執行緒異常結束怎麼解決

【推薦教學:#Java教學##】

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

(1)伴隨這一個業務產生一個比較耗時的任務,而這個業務返回並不需要等待該任務。那我們往往會啟動一個執行緒去完成這個非同步任務。

(2)我們需要一個定時任務例如:定時清除數據,我們會起一個定時執行緒去做該任務。

上述問題比較簡單,new一個執行緒然後去做這件事。但是我們常常忽略一個問題,線程異常了怎麼辦?例如耗時任務我們只完成了一半,我們就異常結束了(這裡不考慮事務一致性,我們只考慮一定要將任務完成)。又比如在清資料的時候,資料庫發生斷連。這時候我們會發現線程死掉了,任務終止了,我們需要重啟整個專案把該定時任務起起來。

解決這些問題的關鍵是,如何捕捉執行緒執行過程中產生的例外狀況?

我們查看JDK API我們會發現在Thread中有setUncaughtExceptionHandler方法,讓我們可以在執行緒發生異常時,呼叫該方法。

Java中執行緒異常結束的解決方法是:

場景一解決思路:

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執行緒異常結束怎麼解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!