摘要:先看寫的簡略的代碼線程池中發現異常,被中斷線程池中發現異常,被中斷我這是一個訂單處理流程,主要用到了一個方法,就是。好了,以上就是對線程池異常捕捉的一個記錄。
開發自己的項目有一段時間了,因為是個長時間跑的服務器端程序,所以異常處理顯得尤為重要。
對于異常的抓取和日志(狹義上的日志)的分析一點都不能落下。
我們使用了Java自帶的Executor模塊,我只是稍微看了下Executors當中三個線程池的實現(策略為:Fixed, Cached, Schedule),其實光看名字就可以了解各自的一些策略信息。OK,這一次我需要一種策略合并Fixed和Cached的兩種特點的自定義Executor。其實很簡單,給Cached設置一個上線就是了。注意他們的同步隊列使用的不同,用LinkedBlockingQueue是個不錯的選擇,至于BlockingQueue的實現可以自行谷歌(以后再記吧)。
先看寫的簡略的代碼
package com.zjseek.recharge.core; import com.zjseek.recharge.exception.SKErrorCode; import com.zjseek.recharge.exception.SKOrderState; import com.zjseek.recharge.model.OrderModel; import com.zjseek.recharge.service.OrderService; import org.apache.log4j.Logger; import java.sql.Timestamp; import java.util.concurrent.*; /** * Created by geminiwen on 14-6-28. */ public class OrderExceptionThreadExecutor extends ThreadPoolExecutor { private Logger logger = Logger.getLogger(OrderExceptionThreadExecutor.class); private OrderService orderService; public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); init(); } public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); init(); } public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); init(); } public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); init(); } private void init() { this.orderService = new OrderService(); } @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Future> f = (Future>) r; try { f.get(); } catch (InterruptedException e) { logger.error("線程池中發現異常,被中斷", e); } catch (ExecutionException e) { logger.error("線程池中發現異常,被中斷", e); } } }
我這是一個訂單處理流程,主要用到了一個protected方法,就是afterExecute。一看這個函數的樣子,想當然的以為如果線程池中出了問題,異常自然回在第二個參數t中傳過來。
也許的確是這樣的,但是這里有一個區別。
我們知道ExecutorServcie中執行一個Runnable有兩個方法,兩個分別是
public void execute(Runnable command); publicFuture submit(Runnable task, T result);
別看接受的參數差不多,其實submit最后是調用的execute的,而且在調用execute前,對task進行了一次封裝,變成了RunnableFuture(它是接口,繼承了Runnable和Future實際是一個實現類FutureTask)。
OK,對于實際操作Runnable的不同,暫時說到這,看下execute方法做了什么事
execute方法對進來的Runnable又包裝成了worker然后進入runWorker
runWorker方法中有這么幾行
try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); }
好了,到了最關鍵的afterExecute這個步驟,我滿心以為這里所有的異常都會通過thrown傳遞進來,看來我還是太年輕了,之前我們分析過,這個Runnable已經被submit封裝成了FutureTask,那么這個task.run()除了我們自己定義的run任務之外,到底還干了啥呢?
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callablec = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
OK,這段源碼摘自FutureTask中的run方法,實際我們自己定義的任務已經變成了Callable:
public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
從它的構造函數就可以看出來。
然后我們在上面實際運行task的地方其實是c.call()這一句。
result = c.call();
我們寫的任務全部在這句代碼里面執行完畢了,看看外面都wrap了啥? OK 我們所有的Throwable全部已經被setException吃掉了,怎么還會拋出到外面那層的execute中呢?
所以我之前實驗的時候,在submit中提交任務無論任務怎么拋異常,在afterExecute中的第二個參數是取不到的,原因就在這。
再回頭看看針對submit改造的函數
protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Future> f = (Future>) r; try { f.get(); } catch (InterruptedException e) { logger.error("線程池中發現異常,被中斷", e); } catch (ExecutionException e) { logger.error("線程池中發現異常,被中斷", e); } }
當然,這里已經默認r是實現Future接口了。通過FutureTask的get方法,能把剛剛setException中的異常給拋出來,這樣我們就能真的拿到這些異常了。
結論如果我們關心線程池執行的結果,則需要使用submit來提交task,那么在afterExecute中對異常的處理也需要通過Future接口調用get方法去取結果,才能拿到異常,如果我們不關心這個任務的結果,可以直接使用ExecutorService中的execute方法(實際是繼承Executor接口)來直接去執行任務,這樣的話,我們的Runnable沒有經過多余的封裝,在runWorker中得到的異常也直接能在afterExecute中捕捉。
好了,以上就是對線程池異常捕捉的一個記錄。想想應該不難,今天也是偶然機會看到的。今天在開發中碰到PHP鎖的問題,頭疼死了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64138.html
摘要:方法可以將當前線程放入等待集合中,并釋放當前線程持有的鎖。此后,該線程不會接收到的調度,并進入休眠狀態。該線程會喚醒,并嘗試恢復之前的狀態。 并發 最近重新復習了一邊并發的知識,發現自己之前對于并發的了解只是皮毛。這里總結以下Java并發需要掌握的點。 使用并發的一個重要原因是提高執行效率。由于I/O等情況阻塞,單個任務并不能充分利用CPU時間。所以在單處理器的機器上也應該使用并發。為...
摘要:通過搜索引擎了解到以下觀點提交到線程池的任務如果拋出異常會導致線程掛掉,遂將提交到線程池的任務中可能出現的異常進行了處理,確實解決了問題。 背景 項目中存在一些定時任務來更新數據庫表,借助了線程池提供的一些能力,線上環境偶爾會出現網絡波動導致服務實例無法連上數據庫,只要出現了這種情況,就會導致數據不會再被更新,通過一些命令發現更新數據庫的線程池中的所有線程都處于waiting狀態。通過...
摘要:本文主要內容為簡單總結中線程池的相關信息。方法簇方法簇用于創建固定線程數的線程池。三種常見線程池的對比上文總結了工具類創建常見線程池的方法,現對三種線程池區別進行比較。 概述 線程可認為是操作系統可調度的最小的程序執行序列,一般作為進程的組成部分,同一進程中多個線程可共享該進程的資源(如內存等)。在單核處理器架構下,操作系統一般使用分時的方式實現多線程;在多核處理器架構下,多個線程能夠...
摘要:去美團面試,問到了什么是線程池,如何使用,為什么要用以下做個總結。二線程池線程池的作用線程池作用就是限制系統中執行線程的數量。真正的線程池接口是。創建固定大小的線程池。此線程池支持定時以及周期性執行任務的需求。 去美團面試,問到了什么是線程池,如何使用,為什么要用,以下做個總結。關于線程之前也寫過一篇文章《高級面試題總結—線程池還能這么玩?》 1、什么是線程池:? java.util...
摘要:去美團面試,問到了什么是線程池,如何使用,為什么要用以下做個總結。二線程池線程池的作用線程池作用就是限制系統中執行線程的數量。真正的線程池接口是。創建固定大小的線程池。此線程池支持定時以及周期性執行任務的需求。 去美團面試,問到了什么是線程池,如何使用,為什么要用,以下做個總結。關于線程之前也寫過一篇文章《高級面試題總結—線程池還能這么玩?》 1、什么是線程池:? java.util...
閱讀 2041·2023-04-25 15:11
閱讀 3462·2021-09-23 11:57
閱讀 1372·2021-07-26 23:38
閱讀 1319·2019-08-30 15:54
閱讀 635·2019-08-30 15:53
閱讀 3245·2019-08-26 13:36
閱讀 986·2019-08-26 12:01
閱讀 2863·2019-08-23 16:21