java 多线程回滚的通用实现

多线程回滚通的通用实现


文章目录

      • 多线程回滚通的通用实现
  • 前言
  • 一、countDownLatch是什么?
  • 二、使用步骤
    • 1.定义主线程抽象类
    • 2.子线程类抽象类
    • 3.信号量类:SignalVariable
    • 3.springbean 上下文工具类:SpringContextUtils
  • 总结


前言

提示:本案例实现了什么?:
例如: 在开发过程中会遇到多线程操作数据库的场景,但是会出现主子线程出现异常回滚,他们之间无法通信,不能全部回滚,以下案例则解决这个问题。


一、countDownLatch是什么?

countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,
当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

二、使用步骤

1.定义主线程抽象类


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("主线程回滚");
        }
    }
}

2.子线程类抽象类

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);
        }
    }
}

3.信号量类:SignalVariable

定义线程同步使用的信号量

public class SignalVariable {
	//回滚信号量
    public static CountDownLatch rollBackLatch ;
    /**
     * countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
     * 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,
     * 当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
     *
     */
    public static CountDownLatch mainThreadLatch;
    /**
     *是否回滚状态位
     */
    static AtomicBoolean rollbackFlag ;
}

3.springbean 上下文工具类:SpringContextUtils

通过这个工具类获取事务的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;
    }

总结

这个案例创建两个抽象方法,用户可以创建过个子线程,和一个主线程,实现多个线程的同步回滚。

你可能感兴趣的:(java,开发语言,后端)