示例代码可以从github上获取 https://github.com/git-simm/simm-framework.git
一、业务场景:
  系统中存在多种场景并发操作事务执行时互锁的情况,导致任务积压,系统崩溃。先做了各场景业务的性能调整,但是并发互锁依然无法避免。于是开始考虑选取调用频繁的同步功能作为死锁的牺牲品,取消执行,释放锁。
 
二、处理方案:
  在这里优先选择FutureTask.cancel方案。核心思想是 调用FutureTask的get方法时,设置超时时长。接收到超时异常后,调用cancel方法,中断线程。当然,实际来看这个方案也满足不了我的业务需要。它存在以下两个局限:
  • cancel方法只是向子线程发起中断请求,是否能够中断取决于子线程自身,不能确定子线程会在哪一步操作退出,加入启用的有事务,这个事务可能回滚了,也可能提交成功了。因此,我们需要借用synchronized功能,让父子线程通讯,来明确获得子线程的运行状态;
  • 子线程中执行数据库操作,引起死锁等待,这种情况下cancle操作是不能取消任务了,只能等到事务超时。这个问题由于cancel无法强制关闭线程,因此无法用FutureTask方案。

  以下实现依然围绕FutureTask这个方案来将,只是添加父子线程通讯,明确获取子线程状态的实现。

三、代码实现:

  3.1、创建一个FTaskEndFlag的线程同步标志。父线程等待子线程反馈执行结果后,再执行后续的逻辑;

package simm.framework.threadutils.interrupt;

import java.util.concurrent.TimeoutException;
/**
* futuretask运行终止事件通知
* 2018.09.22 by simm
*/
public class FTaskEndFlag {
private volatile boolean isNormaled = false;
private volatile boolean fired = false;
private Exception exception =null; public boolean isNormaled() {
return isNormaled;
} /**
* 获取子线程异常信息
* @return
*/
public Exception getException() {
return exception;
} /**
* 通知结束
* @param result
* @param result
*/
public synchronized void notifyEnd(boolean result){
isNormaled = result;
fired = true;
notifyAll();
} /**
* 通知结束
* @param result
* @param result
*/
public synchronized void notifyEnd(boolean result,Exception ex){
isNormaled = result;
exception = ex;
fired = true;
notifyAll();
} /**
* 执行结束通知
*/
public synchronized void waitForEnd() throws InterruptedException {
while (!fired) {
//子线程挂起,释放synchronized同步块
wait();
}
}
/**
* 等待
*/
private void waitFunc(long millis){
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

  3.2、创建一个BaseFutureTask的抽象类,内置FTaskEndFlag线程同步标志;

package simm.framework.threadutils.interrupt;

import java.util.concurrent.Callable;

/**
* 基础任务
* 2018.09.22 by simm
*/
public abstract class BaseFutureTask implements Callable<Boolean> {
/**
* futuretask 等待标志
*/
private FTaskEndFlag flag = new FTaskEndFlag(); public FTaskEndFlag getFlag() {
return flag;
}
}

  3.3、创建一个超时重试的工具类,对FutureTask的结果获取设置超时时间;

package simm.framework.threadutils.interrupt;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.concurrent.*;
/**
* 方法超时重试工具
* 2018.09.20 by simm
*/
public class RetryUtil {
/**
* 可缓存线程执行器(依jvm情况自行回收创建)
*/
private static ExecutorService executorService = Executors.newCachedThreadPool(); /**
* 默认方法(3秒超时,重试3次)
* @param callable
* @return
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public static Boolean execute(BaseFutureTask callable) throws InterruptedException, ExecutionException, TimeoutException {
return execute(callable,3000,1000,3);
} /**
* 方法超时控制
* @param callable 方法体
* @param timeout 超时时长
* @param interval 间隔时长
* @param retryTimes 重试次数
* @return
* @throws ExecutionException
* @throws InterruptedException
* @throws TimeoutException
*/
public static Boolean execute(BaseFutureTask callable, long timeout,long interval, int retryTimes) throws ExecutionException, InterruptedException, TimeoutException {
Boolean result = false;
FutureTask<Boolean> futureTask = new FutureTask<>(callable);
executorService.execute(futureTask);
try {
result = futureTask.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
futureTask.cancel(true);
throw e;
}catch(TimeoutException e){
futureTask.cancel(true);
callable.getFlag().waitForEnd();
if(callable.getFlag().isNormaled()){
return true;
}
e.printStackTrace();
//超时重试
retryTimes--;
if(retryTimes > 0){
Thread.sleep(interval);
execute(callable,timeout,interval,retryTimes);
}else{
throw e;
}
}
return result;
}
}

四、给出一个调用代码。实现一个继承自BaseFutureTask的 FutureTask任务。依旧需要注意子线程涉及到spring的组件,最好是参数从主线程注入到子线程。

RetryUtil.execute(new SyncProductTask(productBiz,productInfo),timeout,interval,3);

参考文章

https://www.jianshu.com/p/55221d045f39

FutureTask子任务取消执行的状态判断的更多相关文章

  1. FutureTask的用法及两种常用的使用场景 + FutureTask的方法执行示意图

    from:  https://blog.csdn.net/linchunquan/article/details/22382487 FutureTask可用于异步获取执行结果或取消执行任务的场景.通过 ...

  2. SpringBoot29 登录逻辑、登录状态判断

    1 知识点扫盲 浏览器和服务器之间时通过session来确定连接状态的,浏览器第一次请求时服务端会自动生成一个session,并将这个sessionId传回给浏览器,浏览器将这个sessionId存放 ...

  3. C# MVC 用户登录状态判断 【C#】list 去重(转载) js 日期格式转换(转载) C#日期转换(转载) Nullable<System.DateTime>日期格式转换 (转载) Asp.Net MVC中Action跳转(转载)

    C# MVC 用户登录状态判断   来源:https://www.cnblogs.com/cherryzhou/p/4978342.html 在Filters文件夹下添加一个类Authenticati ...

  4. 12、pytest -- 缓存:记录执行的状态

    目录 1. cacheprovider插件 1.1. --lf, --last-failed:只执行上一轮失败的用例 1.2. --ff, --failed-first:先执行上一轮失败的用例,再执行 ...

  5. 如何根据执行计划,判断Mysql语句是否走索引

    如何根据执行计划,判断Mysql语句是否走索引

  6. ios想要取消执行延时调用的方法

    想要取消执行延时调用的方法: [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideDia ...

  7. [代码笔记]VUE路由根据返回状态判断添加响应拦截器

    //返回状态判断(添加响应拦截器) Axios.interceptors.response.use( res => { //对响应数据做些事 if (res.data && !r ...

  8. SKU多维属性状态判断算法

    作者:周琪力,前端工程师,网络常用昵称「keelii」.在过去的4年里主要负责京东网站商品详情页的前端系统架构和开发,平时主要写 JavaScript 偶尔写点NodeJS,Python.琪力博客:  ...

  9. 判断密码是否可见/判断登录的状态/判断在form表单中 定义rules规则验证(iview)

    一: 判断密码是否可见判断:type="visiblePassword ? 'text' : 'password'" 是否为false 或者 true 密码为输入框或者文本框点击眼 ...

随机推荐

  1. C#网络编程(同步传输字符串) - Part.2

    服务端客户端通信 在与服务端的连接建立以后,我们就可以通过此连接来发送和接收数据.端口与端口之间以流(Stream)的形式传输数据,因为几乎任何对象都可以保存到流中,所以实际上可以在客户端与服务端之间 ...

  2. linux基础命令复习

    1.ls 查看文件和文件夹 1).ls -a 查看文件和文件夹,包括隐藏的 2).ls  -l 查看文件和文件夹详情 3).ls  -lh   查看文件和文件夹详情,自动生成文件大小单位 4).ls ...

  3. C# VS Java

    摘要:C#的语言规范由Microsoft的Anders Hejlsberg与Scott Wiltamuth编写.在当前Microsoft天花乱坠的宣传中,对C#和C++.Java作一番比较总是很有趣的 ...

  4. mysql实战优化之七:数据库侧配置优化

    对于功能,我们可能知道必须改进什么:但对于性能问题,有时我们可能无从下手.其实,任何计算机应用系统最终队可以归结为: cpu消耗 内存使用 对磁盘,网络或其他I/O设备的输入/输出(I/O)操作. 但 ...

  5. PHP函数htmlspecialchars_decode

    htmlspecialchars_decode() 函数把一些预定义的 HTML 实体转换为字符. <?php $str = "This is some <b>bold&l ...

  6. Python Twisted系列教程5:由Twisted支持的诗歌客户端

    作者:dave@http://krondo.com/twistier-poetry/  译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 抽象地构建客户端 在第四部分中,我们构建了第一个使用 ...

  7. Python中的 set 与 深浅拷贝

    字符串 join() 格式:   "拼接的东西".join(可迭代对象) 可以加列表转换成字符串 lis = ['a','b','c','d'] s = "//" ...

  8. GY89的使用

    GY89集成了三块不同的芯片,分别为:BMP180.L3GD20和LSM303DLH,作用分别是获取温度压强.三轴陀螺仪和加速度计的数据.以下通过把各个模块的数据输出到终端来测试GY89的功能. #i ...

  9. javascript第四节

    闭包: 块级作用域: 私有变量:

  10. DDD学习笔录——提炼问题域之与领域专家一起获得领域见解

    业务和开发团队之间的协作是DDD必不可少的部分,并且它是处于开发阶段的产品获得成功的关键. 领域专家指的是那些从业务领域的政策和工作流程到棘手处和特性都具有深刻理解的人.能够为你的问题区域提供深刻见解 ...