示例代码可以从github上获取 https://github.com/git-simm/simm-framework.git
一、业务场景:
  系统中存在多种场景并发操作事务执行时互锁的情况,导致任务积压,系统崩溃。先做了各场景业务的性能调整,但是并发互锁依然无法避免。于是开始考虑选取调用频繁的同步功能作为死锁的牺牲品,取消执行,释放锁。
 
二、处理方案:
  由于FutureTask.cancel方案无法解决数据库死锁问题。因此接下来切换方案,改为Thread.stop实现,强行杀掉子线程。核心思想是 把需要监控的操作 包装成一个子线程,启动以后主线程开启一个运行时间监控,当判断到子线程已经超出时限,则调用stop方法,强行中断(需要说明一点,stop操作可能会带来一些未知的异常状态,jdk已经将其标记为过时,不建议使用,这里需要个人自行评估方案的影响)。 
  那么只做一个stop子线程的操作,能同时解决掉其对数据库的锁定吗?经过验证这是不可能的,对数据库的锁定需要在事务提交或回滚时 释放。事务是由spring框架自行接管处理的,在发生死锁,子线程未结束的情况下,事务即使到了超时时限,但是由于没有新的sql执行语句触发其超时校验,spring事务也无法自行结束。它的结束点为 数据库事务锁等待超时。因此,现在我们想要让事务提前回滚还需要一个操作,设置 @Transactional的timeout时间,指定为一个较短的时间,子线程被stop后,spring会自动对该事务进行回滚,从而达到数据库解锁的目的。
 

三、代码实现:

  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();
}
}
/**
* 执行结束通知
*/
public void waitForEnd(Thread thread,Long timeout) throws TimeoutException {
long begin = System.currentTimeMillis();
while(System.currentTimeMillis()-begin <= timeout){
waitFunc(10);
//子线程已经执行完毕,则返回
if(fired) return;
}
//超时未返回,则终止线程
try{
thread.stop();
}catch(Exception e){
e.printStackTrace();
throw e;
}
throw new TimeoutException("方法执行超出时限:"+timeout);
}
/**
* 等待
*/
private void waitFunc(long millis){
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

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

package com.zhaogang.ii.biz.threads.future;

/**
* 基础线程
*/
public abstract class BaseThread extends Thread {
/**
* futuretask 等待标志
*/
private FTaskEndFlag flag = new FTaskEndFlag(); public FTaskEndFlag getFlag() {
return flag;
}
}

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

package com.zhaogang.ii.biz.threads.future;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.concurrent.*;
/**
* 方法超时重试工具
* 2018.09.20 by simm
*/
public class RetryUtil {/**
* 默认方法(3秒超时,重试3次)
* @param syncThread
* @param params
* @param <T>
* @return
* @throws Exception
*/
public static <T extends BaseThread> Boolean execute(Class<T> syncThread, List<Object> params) throws Exception {
return execute(syncThread,params,3000,1000,3);
} /**
* 方法超时控制
* @param syncThread 线程类
* @param params 线程构造参数
* @param timeout
* @param interval
* @param retryTimes
* @param <T>
* @return
* @throws Exception
*/
public static <T extends BaseThread> Boolean execute(Class<T> syncThread, List<Object> params, long timeout, long interval, int retryTimes) throws Exception {
Boolean result = false;
try{
//参数类型数组
Class[] parameterTypeArrs = new Class[params.size()];
for(int i=0;i<params.size();i++){
Class c = params.get(i).getClass();
if(c.getName().indexOf("$$")>0){
String clsName = c.getName().substring(0,c.getName().indexOf("$$"));
parameterTypeArrs[i] = Class.forName(clsName);
}else{
parameterTypeArrs[i] = c;
}
}
//根据参数类型获取相应的构造函数
Constructor constructor= syncThread.getConstructor(parameterTypeArrs);
//参数数组
Object[] parameters= params.toArray();
//根据获取的构造函数和参数,创建实例
BaseThread processor = (BaseThread) constructor.newInstance(parameters);
processor.start();
//等待线程结束
processor.getFlag().waitForEnd(processor,timeout);
boolean r = processor.getFlag().isNormaled();
if(!r){
throw processor.getFlag().getException();
}
return processor.getFlag().isNormaled();
}catch (TimeoutException e) {
//超时重试
retryTimes--;
if(retryTimes > 0){
System.out.println("方法开始重试:"+retryTimes);
Thread.sleep(interval);
execute(syncThread,params,timeout,interval,retryTimes);
}else{
throw e;
}
}
return result;
}
}

  3.4、设置子线程事务超时时间;

    @Transactional(timeout = 3)
public void syncProduct(ProductDetailinfo productInfo) {

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

package interrupt;

import simm.framework.threadutils.interrupt.BaseThread;

public class SyncProductThread extends BaseThread {
private ProductBiz productBiz;
private ProductDetailinfo productInfo;
/**
* 构造函数
* @param productBiz
* @param productInfo
*/
public SyncProductThread(ProductBiz productBiz, ProductDetailinfo productInfo){
this.productBiz = productBiz;
this.productInfo = productInfo;
}
@Override
public void run() {
boolean isNormaled = false;
Exception exception = null;
try{
productBiz.syncProduct(productInfo);
isNormaled = true;
}catch(Exception e){
e.printStackTrace();
exception = e;
}finally {
//通知子线程运行完毕了
super.getFlag().notifyEnd(isNormaled,exception);
}
}
}
/**
* 超时中断的同步方法
* @param productInfo
* @throws Exception
*/
public void syncProductTimeout(final ProductDetailinfo productInfo) throws Exception {
Long timeout = 3000L;
Long interval = 1000L;
RetryUtil.execute(SyncProductThread.class,Arrays.asList(productBiz,productInfo),timeout,interval,3);
//RetryUtil.execute(new SyncProductTask(productBiz,productInfo),timeout,interval,3);
}

参考文章

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

利用Thread.stop完成方法执行超时中断的更多相关文章

  1. C# 方法执行超时处理

    封装了一个方法,用于处理一些需要判断是否执行超时了的操作 internal static T TimeoutCheck<T>(int ms, Func<T> func) { v ...

  2. C# 给某个方法设定执行超时时间 C#如何控制方法的执行时间,超时则强制退出方法执行 C#函数运行超时则终止执行(任意参数类型及参数个数通用版)

    我自己写的 /// <summary> /// 函数运行超时则终止执行(超时则返回true,否则返回false) /// </summary> /// <typepara ...

  3. 转 C# 给某个方法设定执行超时时间

    在某些情况下(例如通过网络访问数据),常常不希望程序卡住而占用太多时间以至于造成界面假死. 在这时.我们可以通过Thread.Thread + Invoke(UI)或者是 delegate.Begin ...

  4. java多线程基本概述(二)——Thread的一些方法

    在Thread类中有很多方法值得我们关注一下.下面选取几个进行范例: 1.1.isAlive()方法 java api 描述如下: public final boolean isAlive() Tes ...

  5. 第二节:深入剖析Thread的五大方法、数据槽、内存栅栏。

    一. Thread及其五大方法 Thread是.Net最早的多线程处理方式,它出现在.Net1.0时代,虽然现在已逐渐被微软所抛弃,微软强烈推荐使用Task(后面章节介绍),但从多线程完整性的角度上来 ...

  6. Java基础知识强化之网络编程笔记25:Android网络通信之 Future接口介绍(Java程序执行超时)

    1. Future接口简介 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API ...

  7. [C#.net]SqlDataAdapter 执行超时已过期 完成操作之前已超时或服务器未响应

    随着数据库数据的不断增大,查询时间也随之增长.而客户端与数据库连接时间以及命令的执行时间都是有限的.默认为30s.所以在查询数据的时候,程序会出现 “超时时间已到.在操作完成之前超时时间已过或服务器未 ...

  8. 并发编程之 Thread 类过期方法和常用方法

    前言 在 Java 刚诞生时,Thread 类就已经有了很多方法,但这些方法由于一些原因(有一些明显的bug或者设计不合理)有些已经废弃了,但是他们的方法名却是非常的好,真的是浪费.我们在进行并发必编 ...

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

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

随机推荐

  1. 非常好用的css代码格式化工具

    http://tool.lanrentuku.com/cssformat/ 可以横向排列和竖向排列,感谢互联网,让我找到你了.

  2. Spring aop 记录操作日志 Aspect 自定义注解

    时间过的真快,转眼就一年了,没想到随手写的笔记会被这么多人浏览,不想误人子弟,于是整理了一个优化版,在这里感谢智斌哥提供的建议和帮助,话不多说,进入正题 所需jar包 :spring4.3相关联以及a ...

  3. PCB 锣板和半孔工艺的差别

    PCB 锣板和半孔工艺的差别 PCB 在做模块时会用到半孔工艺,但是由于半孔是特殊工艺. 需要加费用,打板时费还不低. 下面这个图是锣板和半孔工艺的差别. https://www.amobbs.com ...

  4. ClassLoader热加载的简单实现

    当我们在eclipse中修改了一个.java文件时,并通过[ctrl + s ]保存了此java文件,相应的bin目录中,会发现.class文件也发生了修改.通常情况下,java文件是在我们的web项 ...

  5. 验证DataGridView单元格的值

    private void gridPurchaseOrderDetail_CellValidating(object sender, DataGridViewCellValidatingEventAr ...

  6. Java-Maven-Runoob:Maven 构建 & 项目测试

    ylbtech-Java-Maven-Runoob:Maven 构建 & 项目测试 1.返回顶部 1. Maven 构建 & 项目测试 在上一章节中我们学会了如何使用 Maven 创建 ...

  7. thinkphp中的dump方法

    感受一下,调试. 1.print_r() 2.var_dump() 3.再看看thinkphp中的dump方法 清晰多了!真实够傻的,今天才发现有这么好的调试方法.

  8. Oracle常见的表连接的方法

    1 排序合并连接SMJ Sort merge join 排序合并总结: 1 通常情况下,排序合并连接的效率远不如hash join,前者适用范围更广,hj只使用于等值连接,smj范围更广(<,& ...

  9. ajax-解决跨域请求(基于js,jQuery的josnp,设置响应头的cors)

    同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之上的 ...

  10. Windows下搭建PHP开发环境-WEB服务器

    PHP集成开发环境有很多,如XAMPP.AppServ......只要一键安装就把PHP环境给搭建好了.但这种安装方式不够灵活,软件的自由组合不方便,同时也不利于学习.所以我还是喜欢手工搭建PHP开发 ...