欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中捕獲線程異常的幾種方式總結(jié)

 更新時(shí)間:2022年11月24日 15:59:27   作者:潘建南  
這篇文章主要介紹了Java中捕獲線程異常的幾種方式總結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

首先,我們要知道,在Java中,線程中的異常是不能拋出到調(diào)用該線程的外部方法中捕獲的。

為什么不能拋出到外部線程捕獲?

因?yàn)榫€程是獨(dú)立執(zhí)行的代碼片斷,線程的問題應(yīng)該由線程自己來解決,而不要委托到外部。

基于這樣的設(shè)計(jì)理念,在Java中,線程方法的異常都應(yīng)該在線程代碼邊界之內(nèi)(run方法內(nèi))進(jìn)行try catch并處理掉。

換句話說,我們不能捕獲從線程中逃逸的異常。

怎么進(jìn)行的限制?

通過java.lang.Runnable.run()方法聲明(因?yàn)榇朔椒暶魃蠜]有throw exception部分)進(jìn)行了約束。

如果在線程中拋出了線程會(huì)怎么樣?

線程會(huì)立即終結(jié)。

現(xiàn)在我們可以怎樣捕獲線程中的異常?

Java中在處理異常的時(shí)候,通常的做法是使用try-catch-finally來包含代碼塊,但是Java自身還有一種方式可以處理——使用UncaughtExceptionHandler。

它能檢測出某個(gè)線程由于未捕獲的異常而終結(jié)的情況。

當(dāng)一個(gè)線程由于未捕獲異常而退出時(shí),JVM會(huì)把這個(gè)事件報(bào)告給應(yīng)用程序提供的UncaughtExceptionHandler異常處理器(這是Thread類中的接口):

//Thread類中的接口
public interface UncaughtExceptionHanlder {
?? ?void uncaughtException(Thread t, Throwable e);
}

JDK5之后允許我們在每一個(gè)Thread對象上添加一個(gè)異常處理器UncaughtExceptionHandler 。

Thread.UncaughtExceptionHandler.uncaughtException()方法會(huì)在線程因未捕獲的異常而面臨死亡時(shí)被調(diào)用。

首先要先定義一個(gè)異常捕獲器:

public class MyUnchecckedExceptionhandler implements UncaughtExceptionHandler {
? ? @Override
? ? public void uncaughtException(Thread t, Throwable e) {
? ? ? ? System.out.println("捕獲異常處理方法:" + e);
? ? }
}

方法1. 創(chuàng)建線程時(shí)設(shè)置異常處理Handler

Thread t = new Thread(new ExceptionThread());
t.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
t.start();

方法2. 使用Executors創(chuàng)建線程時(shí),還可以在ThreadFactory中設(shè)置

ExecutorService exec = Executors.newCachedThreadPool(new ThreadFactory(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public Thread newThread(Runnable r) {
? ? ? ? ? ? ? ? Thread thread = new Thread(r);
? ? ? ? ? ? ? ? thread.setUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());
? ? ? ? ? ? ? ? return thread;
? ? ? ? ? ? }
});
exec.execute(new ExceptionThread());

不過,上面的結(jié)果能證明:通過execute方式提交的任務(wù),能將它拋出的異常交給異常處理器。

如果改成submit方式提交任務(wù),則異常不能被異常處理器捕獲,這是為什么呢?

查看源碼后可以發(fā)現(xiàn),如果一個(gè)由submit提交的任務(wù)由于拋出了異常而結(jié)束,那么這個(gè)異常將被Future.get封裝在ExecutionException中重新拋出。

所以,通過submit提交到線程池的任務(wù),無論是拋出的未檢查異常還是已檢查異常,都將被認(rèn)為是任務(wù)返回狀態(tài)的一部分,因此不會(huì)交由異常處理器來處理。

java.util.concurrent.FutureTask 源碼

public V get() throws InterruptedException, ExecutionException {
? ? int s = state;
? ? if (s <= COMPLETING)//如果任務(wù)沒有結(jié)束,則等待結(jié)束
? ? ? ? s = awaitDone(false, 0L);
? ? return report(s);//如果執(zhí)行結(jié)束,則報(bào)告執(zhí)行結(jié)果
}

@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
? ? Object x = outcome;
? ? if (s == NORMAL)//如果執(zhí)行正常,則返回結(jié)果
? ? ? ? return (V)x;
? ? if (s >= CANCELLED)//如果任務(wù)被取消,調(diào)用get則報(bào)CancellationException
? ? ? ? throw new CancellationException();
? ? throw new ExecutionException((Throwable)x);//執(zhí)行異常,則拋出ExecutionException
}

方法3. 使用線程組ThreadGroup

//1.創(chuàng)建線程組
ThreadGroup threadGroup =
? ? ? ? // 這是匿名類寫法
? ? ? ? new ThreadGroup("group") {
? ? ? ? ? ? // 繼承ThreadGroup并重新定義以下方法
? ? ? ? ? ? // 在線程成員拋出unchecked exception 會(huì)執(zhí)行此方法
? ? ? ? ? ? @Override
? ? ? ? ? ? public void uncaughtException(Thread t, Throwable e) {
? ? ? ? ? ? ? ? //4.處理捕獲的線程異常
? ? ? ? ? ? }
? ? ? ? };
//2.創(chuàng)建Thread ? ? ? ?
Thread thread = new Thread(threadGroup, new Runnable() {
? ? @Override
? ? public void run() {
? ? ? ? System.out.println(1 / 0);

? ? }
}, "my_thread"); ?
//3.啟動(dòng)線程
thread.start(); ??

方法4. 默認(rèn)的線程異常捕獲器

如果我們只需要一個(gè)線程異常處理器處理線程的異常,那么我們可以設(shè)置一個(gè)默認(rèn)的線程異常處理器,當(dāng)線程出現(xiàn)異常時(shí),

如果我們沒有指定線程的異常處理器,而且線程組也沒有設(shè)置,那么就會(huì)使用默認(rèn)的線程異常處理器

// 設(shè)置默認(rèn)的線程異常捕獲處理器
Thread.setDefaultUncaughtExceptionHandler(new MyUnchecckedExceptionhandler());

上面說的4種方法都是基于線程異常處理器實(shí)現(xiàn)的,接下來將的幾種方法則不需要依賴異常處理器。

方法5. 使用FetureTask來捕獲異常

//1.創(chuàng)建FeatureTask
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
? ? @Override
? ? public Integer call() throws Exception {
? ? ? ? return 1/0;
? ? }
});
//2.創(chuàng)建Thread
Thread thread = new Thread(futureTask);
//3.啟動(dòng)線程
thread.start();
try {
? ? Integer result = futureTask.get();
} catch (InterruptedException e) {
? ? e.printStackTrace();
} catch (ExecutionException e) {
? ? //4.處理捕獲的線程異常
}

方法6. 利用線程池提交線程時(shí)返回的Feature引用

//1.創(chuàng)建線程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//2.創(chuàng)建Callable,有返回值的,你也可以創(chuàng)建一個(gè)線程實(shí)現(xiàn)Callable接口。
// ?如果你不需要返回值,這里也可以創(chuàng)建一個(gè)Thread即可,在第3步時(shí)submit這個(gè)thread。
Callable<Integer> callable = new Callable<Integer>() {
? ? @Override
? ? public Integer call() throws Exception {
? ? ? ? return 1/0;
? ? }
};
//3.提交待執(zhí)行的線程
Future<Integer> future = executorService.submit(callable);
try {
? ? ?Integer result = future.get();
} catch (InterruptedException e) {
? ? e.printStackTrace();
} catch (ExecutionException e) {
? ? //4.處理捕獲的線程異常
}

實(shí)現(xiàn)原理可以看一下方法2的說明。

方法6本質(zhì)上和方法5一樣是基于FutureTask實(shí)現(xiàn)的。

方法7. 重寫ThreadPoolExecutor的afterExecute方法

//1.創(chuàng)建線程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS,
? ? ? ? new LinkedBlockingQueue<>()) {
? ? @Override
? ? protected void afterExecute(Runnable r, Throwable t) {
? ? ? ? if (r instanceof Thread) {
? ? ? ? ? ? if (t != null) {
? ? ? ? ? ? ? ? //處理捕獲的異常
? ? ? ? ? ? }
? ? ? ? } else if (r instanceof FutureTask) {
? ? ? ? ? ? FutureTask futureTask = (FutureTask) r;
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? futureTask.get();
? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? } catch (ExecutionException e) {
? ? ? ? ? ? ? ? //處理捕獲的異常
? ? ? ? ? ? }
? ? ? ? }

? ? }
};


Thread t1 = new Thread(() -> {
? ? int c = 1 / 0;
});
threadPoolExecutor.execute(t1);

Callable<Integer> callable = () -> 2 / 0;
threadPoolExecutor.submit(callable);

總結(jié)

線程最好交由線程池進(jìn)行管理。

線程池中如果你的線程不需要返回值則可以使用方法2,利用ThreadFactory為線程指定統(tǒng)一的異常處理器。記得一定要用execute方式提交任務(wù),否則異常處理器捕獲不到異常。

線程池中如果你的線程需要返回值,你又想捕獲線程異常,則需要借助FutureTask,即使用方法6。使用submit方法提交線程。當(dāng)然了,不需要返回值的情況也可以使用方法6。

方法7同時(shí)支持execute和submit提交任務(wù)時(shí)異常的捕獲,適合相同類型任務(wù)的統(tǒng)一異常處理,但是異常處理粒度較粗,而方法2和方法6可以針對每個(gè)任務(wù)進(jìn)行自定義的異常處理。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中Date與String相互轉(zhuǎn)換的方法

    Java中Date與String相互轉(zhuǎn)換的方法

    這篇文章主要為大家詳細(xì)介紹了Java中Date與String相互轉(zhuǎn)換方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • 詳解Java異常處理中finally子句的運(yùn)用

    詳解Java異常處理中finally子句的運(yùn)用

    這篇文章主要介紹了Java異常處理中finally子句的運(yùn)用,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • Java算法之堆排序代碼示例

    Java算法之堆排序代碼示例

    這篇文章主要介紹了Java算法之堆排序代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • 基于接口實(shí)現(xiàn)java動(dòng)態(tài)代理示例

    基于接口實(shí)現(xiàn)java動(dòng)態(tài)代理示例

    這篇文章主要介紹了基于接口實(shí)現(xiàn)java動(dòng)態(tài)代理示例,需要的朋友可以參考下
    2014-04-04
  • MyBatis分頁查詢返回list的時(shí)候出現(xiàn)null的問題

    MyBatis分頁查詢返回list的時(shí)候出現(xiàn)null的問題

    這篇文章主要介紹了MyBatis分頁查詢返回list的時(shí)候出現(xiàn)null的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Spring?Boot中KafkaListener的介紹、原理和使用方法案例詳解

    Spring?Boot中KafkaListener的介紹、原理和使用方法案例詳解

    本文介紹了Spring Boot中 @KafkaListener 注解的介紹、原理和使用方法,通過本文的介紹,我們希望讀者能夠更好地理解Spring Boot中 @KafkaListener 注解的使用方法,并在項(xiàng)目中更加靈活地應(yīng)用
    2023-09-09
  • springboot中使用jpa下hibernate的ddl-auto方式

    springboot中使用jpa下hibernate的ddl-auto方式

    這篇文章主要介紹了springboot中使用jpa下hibernate的ddl-auto方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • mybatis基本實(shí)例詳解

    mybatis基本實(shí)例詳解

    這篇文章主要介紹了mybatis基本實(shí)例詳解以及mybatis自由模糊查詢,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • 淺談兩個(gè)jar包中包含完全相同的包名和類名的加載問題

    淺談兩個(gè)jar包中包含完全相同的包名和類名的加載問題

    下面小編就為大家?guī)硪黄獪\談兩個(gè)jar包中包含完全相同的包名和類名的加載問題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java中枚舉的使用方法詳解

    Java中枚舉的使用方法詳解

    這篇文章主要介紹了Java中枚舉的使用方法詳解,比如我們想聲明一組季節(jié)的集合,那這里面最多有四種,即春夏秋冬,不允許有其他的季節(jié),那為了實(shí)現(xiàn)這種限制,體現(xiàn)出季節(jié)是固定的四個(gè)對象,我們可以使用枚舉,需要的朋友可以參考下
    2023-07-07

最新評論