上午在生产服务器发现一个不小的问题,就是一个程序在调用存储过程中抢到了锁,但抢到锁后调用存储过程执行出现卡死的情况,导致抢到的锁迟迟没有释放,这导致第二天程序执行时,因为无法获取到锁而无法正常执行。
解决方案:引入Future类,并设定调用存储过程执行的超时时间,通过get(long timeout, TimeUnit unit)
,当抛出超时异常时,记录异常,往下进行其他处理逻辑,并正常释放锁。
当创建了Future实例,任务可能有以下三种状态:
- 等待状态。此时调用
cancel()
方法不管传入true还是false都会标记为取消,任务依然保存在任务队列中,但当轮到此任务运行时会直接跳过。- 完成状态。此时
cancel()
不会起任何作用,因为任务已经完成了。- 运行中。此时传入true会中断正在执行的任务,传入false则不会中断。
总结:
Future.cancel(true)
适用于:
(1) 长时间处于运行的任务,并且能够处理interruption
Future.cancel(false)
适用于:
(1) 未能处理interruption的任务 ;
(2) 不清楚任务是否支持取消 ;
(3) 需要等待已经开始的任务执行完成
吊诡的事情:当超时后,我调用Future的cancel(true)方法,在本地demo测试如下代码时,均可以正常中断任务线程,但在Spring项目工程中使用时,却没有实现效果。这个坑,暂且留下,还待后面再补上。
回复吊诡的事情:这里把坑补上,其实吊诡的事情并不吊诡,主要是我事先没有一个词“能够处理interruption”或是“可中断的方法”。下面解释一下:
当一个方法内部调用了wait、sleep、join等方法时,会使得当前线程进入阻塞状态,若另外的一个线程调用被阻塞线程的interrupt方法,则会打断这种阻塞,因此这种方法有时会被称为可中断方法。记住,打断一个线程并不等于该线程的生命周期结束,仅仅是打断了当前线程的阻塞状态。
而所谓“吊诡的事情”发生,正是因为我在demo测试的代码中调用sleep方法,使得demo测试的代码成为了可中断的方法,而Spring工程中的代码,未调用sleep等类方法,也就是未进入阻塞状态,故而无法被中断。
demo测试代码:
package com.xgh.demo.threaddemo;
import java.util.concurrent.*;
public class TestFuture {
public static void main(String[] args) {
try {
testTimeout3(100);
System.out.println("执行结束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("下面开始判断程序是否超时或已经执行完毕。。。");
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(); //关闭线程池
}
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();
}
/**
* 验证不可中断的方法
* @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("下面开始判断程序是否超时或已经执行完毕。。。");
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(); //关闭线程池
}
}
参考文章:
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