上午在生產(chǎn)服務(wù)器發(fā)現(xiàn)一個(gè)不小的問(wèn)題炕檩,就是一個(gè)程序在調(diào)用存儲(chǔ)過(guò)程中搶到了鎖吁系,但搶到鎖后調(diào)用存儲(chǔ)過(guò)程執(zhí)行出現(xiàn)卡死的情況辕宏,導(dǎo)致?lián)尩降逆i遲遲沒(méi)有釋放,這導(dǎo)致第二天程序執(zhí)行時(shí)宇智,因?yàn)闊o(wú)法獲取到鎖而無(wú)法正常執(zhí)行蔓搞。
解決方案:引入Future類,并設(shè)定調(diào)用存儲(chǔ)過(guò)程執(zhí)行的超時(shí)時(shí)間普筹,通過(guò)get(long timeout, TimeUnit unit)
败明,當(dāng)拋出超時(shí)異常時(shí),記錄異常太防,往下進(jìn)行其他處理邏輯妻顶,并正常釋放鎖。
當(dāng)創(chuàng)建了Future實(shí)例蜒车,任務(wù)可能有以下三種狀態(tài):
- 等待狀態(tài)讳嘱。此時(shí)調(diào)用
cancel()
方法不管傳入true還是false都會(huì)標(biāo)記為取消,任務(wù)依然保存在任務(wù)隊(duì)列中酿愧,但當(dāng)輪到此任務(wù)運(yùn)行時(shí)會(huì)直接跳過(guò)沥潭。- 完成狀態(tài)。此時(shí)
cancel()
不會(huì)起任何作用嬉挡,因?yàn)槿蝿?wù)已經(jīng)完成了钝鸽。- 運(yùn)行中。此時(shí)傳入true會(huì)中斷正在執(zhí)行的任務(wù)庞钢,傳入false則不會(huì)中斷拔恰。
總結(jié):
Future.cancel(true)
適用于:
(1) 長(zhǎng)時(shí)間處于運(yùn)行的任務(wù),并且能夠處理interruption
Future.cancel(false)
適用于:
(1) 未能處理interruption的任務(wù) ;
(2) 不清楚任務(wù)是否支持取消 ;
(3) 需要等待已經(jīng)開(kāi)始的任務(wù)執(zhí)行完成
吊詭的事情:當(dāng)超時(shí)后基括,我調(diào)用Future的cancel(true)方法颜懊,在本地demo測(cè)試如下代碼時(shí),均可以正常中斷任務(wù)線程,但在Spring項(xiàng)目工程中使用時(shí)河爹,卻沒(méi)有實(shí)現(xiàn)效果匠璧。這個(gè)坑,暫且留下咸这,還待后面再補(bǔ)上夷恍。
回復(fù)吊詭的事情:這里把坑補(bǔ)上,其實(shí)吊詭的事情并不吊詭炊苫,主要是我事先沒(méi)有一個(gè)詞“能夠處理interruption”或是“可中斷的方法”裁厅。下面解釋一下:
當(dāng)一個(gè)方法內(nèi)部調(diào)用了wait、sleep侨艾、join等方法時(shí),會(huì)使得當(dāng)前線程進(jìn)入阻塞狀態(tài)拓挥,若另外的一個(gè)線程調(diào)用被阻塞線程的interrupt方法唠梨,則會(huì)打斷這種阻塞,因此這種方法有時(shí)會(huì)被稱為可中斷方法侥啤。記住当叭,打斷一個(gè)線程并不等于該線程的生命周期結(jié)束,僅僅是打斷了當(dāng)前線程的阻塞狀態(tài)盖灸。
而所謂“吊詭的事情”發(fā)生蚁鳖,正是因?yàn)槲以赿emo測(cè)試的代碼中調(diào)用sleep方法,使得demo測(cè)試的代碼成為了可中斷的方法赁炎,而Spring工程中的代碼醉箕,未調(diào)用sleep等類方法,也就是未進(jìn)入阻塞狀態(tài)徙垫,故而無(wú)法被中斷讥裤。
demo測(cè)試代碼:
package com.xgh.demo.threaddemo;
import java.util.concurrent.*;
public class TestFuture {
public static void main(String[] args) {
try {
testTimeout3(100);
System.out.println("執(zhí)行結(jié)束3。姻报。己英。。");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public static void testTimeout1(final int num) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newScheduledThreadPool(5);
Future result1 = executor.submit(new Callable() {
@Override
public Integer call() throws Exception {
Thread.sleep(10000);
System.out.println(num + "-22222222222222" + Thread.currentThread().getName());
return num;
}
});
System.out.println("下面開(kāi)始判斷程序是否超時(shí)或已經(jīng)執(zhí)行完畢吴旋。损肛。。");
long currentTimeMillis = System.currentTimeMillis();
long timeout = 5 * 1000L;
while (!result1.isDone()) {
long timecha = System.currentTimeMillis() - currentTimeMillis;
if (timecha >= timeout) {
System.out.println("revoke timeout");
boolean cancel = result1.cancel(true);
System.out.println("revoke cancel result : --->" + cancel + " ;result isCancelled: " + result1.isCancelled());
cancel = result1.cancel(true);
System.out.println("revoke cancel result2 : --->" + cancel + " ;result isCancelled: " + result1.isCancelled());
break;
}
if (result1.isDone()) {
System.out.println("result1----->" + result1.get());
System.out.println("revoke success...");
boolean cancel = result1.cancel(true);
System.out.println("revoke cancel result : --->" + cancel);
break;
}
}
executor.shutdown(); //關(guān)閉線程池
}
public static void testTimeout2(final int num) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newScheduledThreadPool(5);
Future result1 = executor.submit(new Callable() {
@Override
public Boolean call() throws Exception {
Thread.sleep(10000);
System.out.println(num + "=========" + Thread.currentThread().getName());
return true;
}
});
long timeout = 5 * 1000L;
try {
boolean result = (boolean) result1.get(timeout, TimeUnit.MILLISECONDS);
System.out.println("revoke success荣瑟,result ----->" + result);
} catch (TimeoutException e) {
System.out.println("revoke timeout:" + e);
boolean cancel = result1.cancel(true);
System.out.println("revoke cancel result : --->" + cancel + " ;result isCancelled: " + result1.isCancelled());
}
executor.shutdown();
}
/**
* 驗(yàn)證不可中斷的方法
* @param num
* @throws InterruptedException
* @throws ExecutionException
*/
public static void testTimeout3(final int num) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newScheduledThreadPool(5);
Future result1 = executor.submit(new Callable() {
@Override
public Integer call() throws Exception {
int i= 1;
while (true){
System.out.println(num + "-22222222222222" + Thread.currentThread().getName()+"=="+Thread.currentThread().isInterrupted());
i ++;
if (i == 99999999){
break;
}
}
return num;
}
});
System.out.println("下面開(kāi)始判斷程序是否超時(shí)或已經(jīng)執(zhí)行完畢治拿。。褂傀。");
long currentTimeMillis = System.currentTimeMillis();
long timeout = 2000L;
while (!result1.isDone()) {
long timecha = System.currentTimeMillis() - currentTimeMillis;
if (timecha >= timeout) {
System.out.println("revoke timeout");
boolean cancel = result1.cancel(true);
System.out.println("revoke cancel result : --->" + cancel + " ;result isCancelled: " + result1.isCancelled());
cancel = result1.cancel(true);
System.out.println("revoke cancel result2 : --->" + cancel + " ;result isCancelled: " + result1.isCancelled());
break;
}
if (result1.isDone()) {
System.out.println("result1----->" + result1.get());
System.out.println("revoke success...");
boolean cancel = result1.cancel(true);
System.out.println("revoke cancel result : --->" + cancel);
break;
}
}
executor.shutdown(); //關(guān)閉線程池
}
}
參考文章:
https://felord.blog.csdn.net/article/details/104788189
https://blog.csdn.net/u014252478/article/details/82109694
https://blog.csdn.net/qq_24630433/article/details/88537407