提示:本案例实现了什么?:
例如: 在开发过程中会遇到多线程操作数据库的场景,但是会出现主子线程出现异常回滚,他们之间无法通信,不能全部回滚,以下案例则解决这个问题。
countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,
当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
execute方法需要用户重写,用户隐式创建对象后调用run方法,在创建对象时构造方法将子线程作为数组传入。在下面测试方法中详细说明。
public abstract class BaseMainThread {
@Resource
public PlatformTransactionManager transactionManager;
/**
* 进程集合
*/
List<Future<String>> list = new ArrayList<Future<String>>();
List<Runnable> threadList;
String result = "";
protected BaseMainThread(List<Runnable> threadList) {
SignalVariable.rollBackLatch=new CountDownLatch(1);
SignalVariable.rollbackFlag = new AtomicBoolean(false);
SignalVariable.mainThreadLatch = new CountDownLatch(threadList.size());
this.threadList = threadList;
transactionManager= (PlatformTransactionManager) SpringContextUtils.getBean("transactionManager");
}
protected BaseMainThread() {
}
;
/**
* 主线程业务
*/
public abstract void MainExecute();
public void run() {
// 设置一个事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 事物隔离级别,开启新事务,这样会比较安全些。
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
MainExecute();
// 获得事务状态
TransactionStatus status = transactionManager.getTransaction(def);
//遍历子线程使用线程池执行
threadList.forEach(item -> {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(item);
});
try {
//阻塞主线程等待子线程完成后继续执行
SignalVariable.mainThreadLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//子线程执行完毕判断回滚状态位是否启动 如果为false 则进入方法
if (!SignalVariable.rollbackFlag.get()) {
try {
System.out.println("主线程提交");
//将回滚状态位 置为0 则子线程阻塞状态继续执行
SignalVariable.rollBackLatch.countDown();
for (Future<String> f : list) {
if (!"success".equals(f.get())) {
result = f.get() + "。";
System.out.println(result);
}
}
transactionManager.commit(status);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} else {
//如果会滚状态位位ture 执行回滚
transactionManager.rollback(status);
System.out.println("主线程回滚");
}
}
}
SubThread 子线程的使用需要隐式实现接口 重写execute 指定子线程执行内容,并放入List中
public abstract class SubThread implements Runnable {
@Resource
private PlatformTransactionManager transactionManager;
protected SubThread(){
transactionManager= (PlatformTransactionManager) SpringContextUtils.getBean("transactionManager");
}
/**
* 子线程执行内容
*/
public abstract void execute();
@Override
public void run() {
if (SignalVariable.rollbackFlag.get()) {
return;
// 如果其他线程已经报错 就停止线程
}
// 设置一个事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
// 事物隔离级别,开启新事务,这样会比较安全些。
TransactionStatus status = transactionManager.getTransaction(def);
// 获得事务状态
try {
// 业务处理开始
execute();
// 业务处理结束 释放一个锁
SignalVariable.mainThreadLatch.countDown();
SignalVariable.rollBackLatch.await();// 线程等待
if (SignalVariable.rollbackFlag.get()) {
System.out.println("2进行回滚");
transactionManager.rollback(status);
} else {
System.out.println("子线程执行提交");
transactionManager.commit(status);
}
} catch (Exception e) {
System.out.println("2执行出错");
e.printStackTrace();
// 如果出错了 就放开锁 让别的线程进入提交/回滚 本线程进行回滚
SignalVariable.rollbackFlag.set(true);
SignalVariable.rollBackLatch.countDown();
SignalVariable.mainThreadLatch.countDown();
transactionManager.rollback(status);
}
}
}
定义线程同步使用的信号量
public class SignalVariable {
//回滚信号量
public static CountDownLatch rollBackLatch ;
/**
* countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
* 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,
* 当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
*
*/
public static CountDownLatch mainThreadLatch;
/**
*是否回滚状态位
*/
static AtomicBoolean rollbackFlag ;
}
通过这个工具类获取事务的bean
@Component
public class SpringContextUtils implements ApplicationContextAware {
public static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}
public static Class<? extends Object> getType(String name) {
return applicationContext.getType(name);
}
}
4.测试方法
public SysArticlesEntity test() throws InterruptedException {
//创建子线程列表
List<Runnable> threadList=new ArrayList<Runnable>();
//隐式创建一个线程
SubThread s1=new SubThread() {
/**
* 子线程执行内容
*/
@Override
public void execute() {
System.out.println("执行线程子1");
}
};
//隐式创建一个线程
SubThread s2=new SubThread() {
/**
* 子线程执行内容
*/
@Override
public void execute() {
System.out.println("执行线程子2");
}
};
/** 将子线程放入线程组*/
threadList.add(s2);
threadList.add(s1);
/*声明主线程并放入子线程*/
BaseMainThread mainThread =new BaseMainThread(threadList) {
/**
* 主线程业务
*/
@Override
public void MainExecute() {
System.out.println("执行线程主线程");
baseDao.selectById(2);
}
};
mainThread.run();
SysArticlesEntity dto = baseDao.selectById(2);
return dto;
}
这个案例创建两个抽象方法,用户可以创建过个子线程,和一个主线程,实现多个线程的同步回滚。