執行緒中斷機制,精確控制方法執行
在Java中,有沒有一種方式,可以使正在執行的方法,終止執行呢?
比如說:一個方法,我只想讓它執行10分鐘,超過十分鐘,就馬上停止執行。執行了多少,未執行多少,就記錄下它的執行狀態即可。
今天,我就跟大家分享一個方案。
在這裡:
1、使用JDK中提供的Callable和Future幫助實現;
2、使用Spring託管管理執行緒池;
3、控制方法執行10分鐘,10分鐘內執行完,正常返回;10分鐘之外,報錯超時,執行中斷操作。
先看一段虛擬碼:
package com.creditease.monitor; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; @Service public class CheckService { /** 執行緒池 */ @Autowired private ThreadPoolTaskExecutor taskExecutor; private final Logger logger = LoggerFactory.getLogger(CheckService.class); public void check(){ CheckTask checkTask = new CheckTask(); Future<String> futureTask = taskExecutor.submit(checkTask); try { futureTask.get(10, TimeUnit.MINUTES); } catch (InterruptedException e) { //中斷時的處理 logger.error("執行了中斷處理", e); } catch (ExecutionException e) { //方法報錯 logger.error("方法報錯",e); } catch (TimeoutException e) { //超時 logger.error("方法超時",e); }finally { //方法執行完時,設定中斷 futureTask.cancel(true); } } class CheckTask implements Callable<String>{ @Override public String call() throws Exception { String strReturn = null; List<String> list = Arrays.asList("one","two","three","four","five","six","seven","eight"); Iterator<String> iterator = list.iterator(); //如果執行緒處於中斷狀態,則立即返回 //TODO:do something more than 10mins while (iterator.hasNext() && !Thread.currentThread().isInterrupted()) { //TODO:do someting to get the result 'strReturn' break; } return strReturn; } } }
相應的執行緒池配置:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 核心執行緒數 --> <property name="corePoolSize" value="10" /> <!-- 最大執行緒數 --> <property name="maxPoolSize" value="30" /> <!-- 佇列最大長度 >=mainExecutor.maxSize 預設為 Integer.MAX_VALUE--> <property name="queueCapacity" value="1000" /> <!-- 執行緒池維護執行緒所允許的空閒時間 --> <property name="keepAliveSeconds" value="300" /> <!-- 執行緒池對拒絕任務(無執行緒可用)的處理策略 --> <property name="rejectedExecutionHandler"> <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" /> </property> </bean>
分析一下:
在這段程式中,具體執行邏輯在CheckTask類中,執行緒池taskExecutor提交任務後,進入等待狀態。
futureTask.get(10,TimeUnit.NINUTES)執行,獲取執行結果時,主執行緒進入等待狀態,具體執行邏輯的執行緒被喚醒執行邏輯。
如果執行邏輯10分鐘內,成功獲取到結果,那麼方法正常返回;
如果方法執行,超過10分鐘,則主執行緒被強制喚醒,並返回超時結果,即執行TimeoutException部分。
與此通知,注意與此同時:這時執行finally塊兒中的futureTask.cancel(true)方法。這是主執行緒向執行邏輯執行緒傳送中斷訊號。
注意:這時,在具體執行邏輯中,Thread.currentThread().isInterrupted()方法將變為true。也就是說,這是具體執行邏輯類的方法,也將終止執行並返回。
其實,這裡就是我想跟大家分享的關鍵點:執行緒中斷。
這裡如果沒有在finally塊兒中,執行futureTask.cancel(true),也就是這樣:
//方法執行完時,設定中斷
//futureTask.cancel(true);
這樣,執行結果將會如何呢?
很簡單,如果超過十分鐘,具體邏輯執行緒還未執行完的話,那麼主執行緒依然會超時返回;而具體邏輯執行緒,則會將未執行完的任務執行完。也許是15分鐘,也許是半個小時。也就是說,future.get()方法,能夠保證一定時間內返回,但是Java中並沒有給出一個執行緒kill掉另一個執行緒的方法。
注意:方法超時時,futureTask.get()並不能獲取正常的結果返回了,這是Java中斷機制所限制的,具體結果,可以通過其他方案彌補解決。
但是,Java提供了一種中斷機制,需要開發人員輔助,來完成這個控制操作。
注意:如果在CheckTask類中,while邏輯迴圈中,存在 Thread.sleep()方法,以及wait()方法,那麼我們就可以不使用中斷機制了,也能達到執行緒中斷的目的。原因就是,Java的執行緒中斷機制中,執行執行緒收到中斷訊號,如果執行緒未處於啟用狀態,就會執行緒就會被中斷。