CompletableFuture异步编程事务及多数据源配置详解(含gitee源码)

这篇具有很好参考价值的文章主要介绍了CompletableFuture异步编程事务及多数据源配置详解(含gitee源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

仓库地址: buxingzhe: 一个多数据源和多线程事务练习项目

小伙伴们在日常编码中经常为了提高程序运行效率采用多线程编程,在不涉及事务的情况下,使用dou.lea大神提供的CompletableFuture异步编程利器,它提供了许多优雅的api,我们可以很方便的进行异步多线程编程,速度杠杠的,在这里感谢大佬可怜我们广大码农的不易,提供了如此优秀的异步编程框架!

       刚才说了,不涉及事务情况下,用着爽歪歪,一旦涉及到事务,没有遇到这种情况的就头疼了,多个线程之间发生异常,怎么回滚事务?因为很多业务场景使用了多线程编程,涉及到DML操作(select、update、insert、delete)中的增删改,必须要保持数据在业务上的一致性,比如修改A表,插入B表,这两步在业务上必须是原子的,有一个失败,对于另外表的操作都必须回滚,而spring中对不同线程的数据库连接是单独的,放在ThreadLocal中,多个线程之间不共享事务,下面通过几个浅显易懂的示例,来解释不同场景下的多线程报错以及处理办法。

事务中使用compleablefuture,java,开发语言

 事务中使用compleablefuture,java,开发语言

事务中使用compleablefuture,java,开发语言

 可以看到,子线程中写了抛出异常代码,但是控制台没有打印出,主线程和子线程事务都未回滚,数据正常插入,主线程没有等子线程执行完就结束。对上面的例子修改下:

事务中使用compleablefuture,java,开发语言

 主线程中加入了join(),等待子线程执行,这时控制台打印了子线程抛出的异常如下:

事务中使用compleablefuture,java,开发语言

 数据库数据如下:

事务中使用compleablefuture,java,开发语言

我们看到,主线程方法上由于加了 @Transactional(rollbackFor = Exception.class)声明式事务注解,事务回滚了,数据并没有插入。子线程虽然抛出异常,但是事务没有回滚,数据正常插入了!这不是我们想要的结果,再继续改进下:

先注入一个事务管理器

事务中使用compleablefuture,java,开发语言

 然后在子线程中加入编程式事务代码,手动管理子线程事务状态,发生异常后,回滚子线程事务,并抛出异常至主线程中(直接贴代码了,方便复制粘贴)

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insert() {
        System.out.println("主线程为:"+Thread.currentThread().getName());
        List<User> list = new ArrayList<>(){{
            add(new User("1","张三"));
        }};
        String sql = "insert into user(id,name) values (?,?)";
        jdbcTemplate.batchUpdate(sql,list, list.size(), (ps,d)->{
            ps.setString(1, d.getId());
            ps.setString(2,d.getName());
        });
        //多线程异步操作
        CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 事物隔离级别,开启新事务,这样会比较安全些。
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 获得事务状态
            TransactionStatus status = dataSourceTransactionManager.getTransaction(def);
            try {
                System.out.println("子线程1为:"+Thread.currentThread().getName());
                List<User> syncList = new ArrayList<>(){{
                    add(new User("2","李四"));
                }};
                jdbcTemplate.batchUpdate("insert into user(id,name) values (?,?)",syncList, syncList.size(), (ps,d)->{
                    ps.setString(1, d.getId());
                    ps.setString(2,d.getName());
                });
                //此异常必抛出,模拟抛出异常
                if(1<2){
                    throw new RuntimeException("子线程发生异常");
                }
                //开启手动事务管理后,必须手动在逻辑结束时提交事务,否则会造成锁表,查询可以,增删改会卡住,除非重启服务断开与数据库的连接
                dataSourceTransactionManager.commit(status);
            }catch (Exception e){
                //发生异常时手动回滚子线程事务
                dataSourceTransactionManager.rollback(status);
                //抛出异常供主线程捕获
                throw new RuntimeException(e.getMessage());
            }
        }).exceptionally(throwable -> {
            throw new RuntimeException(throwable.getCause().getMessage());
        });
        //必须等待子线程执行完,抛出异常才能回滚主线程的事务
        future.join();
    }

执行结果如下:

事务中使用compleablefuture,java,开发语言

 数据库数据如下:

事务中使用compleablefuture,java,开发语言

 可以看到主线程和子线程中的数据都回滚了!

以上都还是较为简单的场景,那如果异常是在主线程中发生或者在其他子线程发生,那所有线程中的事务如何回滚呢?请看示例

@Override
    @Transactional(rollbackFor = Exception.class)
    public void insert() {
        System.out.println("主线程为:"+Thread.currentThread().getName());
        List<User> list = new ArrayList<>(){{
            add(new User("1","张三"));
        }};
        String sql = "insert into user(id,name) values (?,?)";
        jdbcTemplate.batchUpdate(sql,list, list.size(), (ps,d)->{
            ps.setString(1, d.getId());
            ps.setString(2,d.getName());
        });
        //线程一抛出异常
        CompletableFuture<Void> futureOne = getFutureOne();
        //线程二无异常
        CompletableFuture<Void> futureTwo = getFutureTwo();
        //必须等待所有子线程执行完,抛出异常才能回滚主线程的事务
        CompletableFuture.allOf(futureOne,futureTwo).join();
    }

    public CompletableFuture<Void> getFutureOne(){
        return CompletableFuture.runAsync(()->{
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 事物隔离级别,开启新事务,这样会比较安全些。
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 获得事务状态
            TransactionStatus status = dataSourceTransactionManager.getTransaction(def);
            try {
                System.out.println("子线程1为:"+Thread.currentThread().getName());
                List<User> syncList = new ArrayList<>(){{
                    add(new User("2","李四"));
                }};
                jdbcTemplate.batchUpdate("insert into user(id,name) values (?,?)",syncList, syncList.size(), (ps,d)->{
                    ps.setString(1, d.getId());
                    ps.setString(2,d.getName());
                });
                //此异常必抛出,模拟抛出异常
                if(1<2){
                    throw new RuntimeException("子线程1发生异常");
                }
                //开启手动事务管理后,必须手动在逻辑结束时提交事务,否则会造成锁表,查询可以,增删改会卡住,除非重启服务,断开与数据库的连接
                dataSourceTransactionManager.commit(status);
            }catch (Exception e){
                //发生异常时手动回滚子线程事务
                dataSourceTransactionManager.rollback(status);
                //抛出异常供主线程捕获
                throw new RuntimeException(e.getMessage());
            }
        }).exceptionally(throwable -> {
            throw new RuntimeException(throwable.getCause().getMessage());
        });
    }

    public CompletableFuture<Void> getFutureTwo(){
        return CompletableFuture.runAsync(()->{
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 事物隔离级别,开启新事务,这样会比较安全些。
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 获得事务状态
            TransactionStatus status = dataSourceTransactionManager.getTransaction(def);
            try {
                System.out.println("子线程2为:"+Thread.currentThread().getName());
                List<User> syncList = new ArrayList<>(){{
                    add(new User("3","王五"));
                }};
                jdbcTemplate.batchUpdate("insert into user(id,name) values (?,?)",syncList, syncList.size(), (ps,d)->{
                    ps.setString(1, d.getId());
                    ps.setString(2,d.getName());
                });
                //开启手动事务管理后,必须手动在逻辑结束时提交事务,否则会造成锁表,查询可以,增删改会卡住,除非重启服务,断开与数据库的连接
                dataSourceTransactionManager.commit(status);
            }catch (Exception e){
                //发生异常时手动回滚子线程事务
                dataSourceTransactionManager.rollback(status);
                //抛出异常供主线程捕获
                throw new RuntimeException(e.getMessage());
            }
        }).exceptionally(throwable -> {
            throw new RuntimeException(throwable.getCause().getMessage());
        });
    }

上面的代码中主线程调用了两个异步子线程,其中子线程一抛出异常,子线程二无异常,主线程阻塞等待两个子线程执行结果

事务中使用compleablefuture,java,开发语言

 可以看到异常打印在控制台,且只有主线程和线程一的数据回滚

事务中使用compleablefuture,java,开发语言

下面再修改下,实现当子线程有异常抛出时,保证主线程和其他子线程也同步回滚:

 @Override
    @Transactional(rollbackFor = Exception.class)
    public void insert() {
        List<TransactionStatus> statusList = new Vector<>();
        try {
            System.out.println("主线程为:"+Thread.currentThread().getName());
            List<User> list = new ArrayList<>(){{
                add(new User("1","张三"));
            }};
            String sql = "insert into user(id,name) values (?,?)";
            jdbcTemplate.batchUpdate(sql,list, list.size(), (ps,d)->{
                ps.setString(1, d.getId());
                ps.setString(2,d.getName());
            });
            //线程一抛出异常
            CompletableFuture<Void> futureOne = getFutureOne(statusList);
            //线程二无异常
            CompletableFuture<Void> futureTwo = getFutureTwo(statusList);
            //必须等待所有子线程执行完,抛出异常才能回滚主线程的事务
            CompletableFuture.allOf(futureOne,futureTwo).join();
            statusList.forEach(dataSourceTransactionManager::commit);
        }catch (Exception e){
            statusList.forEach(dataSourceTransactionManager::rollback);
            throw new RuntimeException(e.getMessage());
        }
    }

    public CompletableFuture<Void> getFutureOne(List<TransactionStatus> statusList){
        return CompletableFuture.runAsync(()->{
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 事物隔离级别,开启新事务,这样会比较安全些。
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 获得事务状态
            TransactionStatus status = dataSourceTransactionManager.getTransaction(def);
            try {
                System.out.println("子线程1为:"+Thread.currentThread().getName());
                List<User> syncList = new ArrayList<>(){{
                    add(new User("2","李四"));
                }};
                jdbcTemplate.batchUpdate("insert into user(id,name) values (?,?)",syncList, syncList.size(), (ps,d)->{
                    ps.setString(1, d.getId());
                    ps.setString(2,d.getName());
                });
                //此异常必抛出,模拟抛出异常
                if(1<2){
                    throw new RuntimeException("子线程1发生异常");
                }
                //dataSourceTransactionManager.commit(status);
            }catch (Exception e){
                //抛出异常供主线程捕获
                //dataSourceTransactionManager.rollback(status);
                throw new RuntimeException(e.getMessage());
            }finally {
                statusList.add(status);
            }
        }).exceptionally(throwable -> {
            throw new RuntimeException(throwable.getCause().getMessage());
        });
    }

    public CompletableFuture<Void> getFutureTwo(List<TransactionStatus> statusList){
        return CompletableFuture.runAsync(()->{
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 事物隔离级别,开启新事务,这样会比较安全些。
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 获得事务状态
            TransactionStatus status = dataSourceTransactionManager.getTransaction(def);
            try {
                System.out.println("子线程2为:"+Thread.currentThread().getName());
                List<User> syncList = new ArrayList<>(){{
                    add(new User("3","王五"));
                }};
                jdbcTemplate.batchUpdate("insert into user(id,name) values (?,?)",syncList, syncList.size(), (ps,d)->{
                    ps.setString(1, d.getId());
                    ps.setString(2,d.getName());
                });
                //dataSourceTransactionManager.commit(status);
            }catch (Exception e){
                //dataSourceTransactionManager.rollback(status);
                throw new RuntimeException(e.getMessage());
            }finally {
                statusList.add(status);
            }
        }).exceptionally(throwable -> {
            throw new RuntimeException(throwable.getCause().getMessage());
        });
    }

用线程安全的集合Vector收集子线程的事务状态,子线程不做commit和rollback,调用后报错如下:

事务中使用compleablefuture,java,开发语言

 No value for key [HikariDataSource (HikariPool-1)] bound to thread [main]
解释: 无法在当前线程绑定的threadLocal中寻找到HikariDataSource作为key,对应关联的资源对象ConnectionHolder

spring中一次事务的完成通常都是默认在当前线程内完成的,又因为一次事务的执行过程中,涉及到对当前数据库连接Connection的操作,因此为了避免将Connection在事务执行过程中来回传递,我们可以将Connextion绑定到当前事务执行线程对应的ThreadLocalMap内部,顺便还可以将一些其他属性也放入其中进行保存,在Spring中,负责保存这些ThreadLocal属性的实现类由TransactionSynchronizationManager承担。

TransactionSynchronizationManager类内部默认提供了下面六个ThreadLocal属性,分别保存当前线程对应的不同事务资源:

   //保存当前事务关联的资源--默认只会在新建事务的时候保存当前获取到的DataSource和当前事务对应Connection的映射关系--当然这里Connection被包装为了ConnectionHolder
	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");
    //事务监听者--在事务执行到某个阶段的过程中,会去回调监听者对应的回调接口(典型观察者模式的应用)---默认为空集合
	private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");
   //见名知意: 存放当前事务名字
	private static final ThreadLocal<String> currentTransactionName =
			new NamedThreadLocal<>("Current transaction name");
   //见名知意: 存放当前事务是否是只读事务
	private static final ThreadLocal<Boolean> currentTransactionReadOnly =
			new NamedThreadLocal<>("Current transaction read-only status");
   //见名知意: 存放当前事务的隔离级别
	private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
			new NamedThreadLocal<>("Current transaction isolation level");
   //见名知意: 存放当前事务是否处于激活状态
	private static final ThreadLocal<Boolean> actualTransactionActive =
			new NamedThreadLocal<>("Actual transaction active");

那么上面抛出的异常的原因也就很清楚了,无法在main线程找到当前事务对应的资源,原因如下:

主线程为:http-nio-5566-exec-2
子线程1为:ForkJoinPool.commonPool-worker-1
子线程2为:ForkJoinPool.commonPool-worker-2

开启新事务时,事务相关资源都被绑定到了http-nio-5566-exec-2线程对应的threadLocalMap内部,而当执行事务提交代码时,commit内部需要从TransactionSynchronizationManager中获取当前事务的资源,显然我们无法从main线程对应的threadLocalMap中获取到对应的事务资源,这就是异常抛出的原因。

下面介绍一种可用的多线程事务回滚方式,但是对编程顺序有要求,小伙伴们可以按需使用。

首先提供一个多线程事务管理类:

import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 多线程事务管理器
 * @Title: MultiThreadingTransactionManager
 * @Description: TODO
 * @author: hulei
 * @date: 2023/8/7 11:36
 * @Version: 1.0
 */
@Slf4j
public class MultiThreadingTransactionManager {

    /**
     * 事务管理器
     */
    private final PlatformTransactionManager transactionManager;

    /**
     * 超时时间
     */
    private final long timeout;

    /**
     * 时间单位
     */
    private final TimeUnit unit;

    /**
     * 一阶段门闩,(第一阶段的准备阶段),当所有子线程准备完成时(除“提交/回滚”操作以外的工作都完成),countDownLatch的值为0
     */
    private CountDownLatch oneStageLatch = null;

    /**
     * 二阶段门闩,(第二阶段的执行执行),主线程将不再等待子线程执行,直接判定总的任务执行失败,执行第二阶段让等待确认的线程进行回滚
     */
    private final CountDownLatch twoStageLatch = new CountDownLatch(1);

    /**
     * 是否提交事务,默认是true(当任一线程发生异常时,isSubmit会被设置为false,即回滚事务)
     */
    private final AtomicBoolean isSubmit = new AtomicBoolean(true);

    /**
     * 构造方法
     *
     * @param transactionManager 事务管理器
     * @param timeout            超时时间
     * @param unit               时间单位
     */
    public MultiThreadingTransactionManager(PlatformTransactionManager transactionManager, long timeout, TimeUnit unit) {
        this.transactionManager = transactionManager;
        this.timeout = timeout;
        this.unit = unit;
    }

    /**
     * 线程池方式执行任务,可保证线程间的事务一致性
     *
     * @param runnableList 任务列表
     * @param executor     线程池
     */
    public void execute(List<Runnable> runnableList, ExecutorService executor) {
        // 排除null值
        runnableList.removeAll(Collections.singleton(null));
        // 属性初始化
        innit(runnableList.size());
        // 遍历任务列表并放入线程池
        for (Runnable runnable : runnableList) {
            // 创建线程
            Thread thread = new Thread(() -> {
                // 如果别的线程执行失败,则该任务就不需要再执行了
                if (!isSubmit.get()) {
                    log.info("当前子线程执行中止,因为线程事务中有子线程执行失败");
                    oneStageLatch.countDown();
                    return;
                }
                // 开启事务
                TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
                try {
                    // 执行业务逻辑
                    runnable.run();
                } catch (Exception e) {
                    // 执行体发生异常,设置回滚
                    isSubmit.set(false);
                    log.error("线程{}:业务发生异常,执行体:{}", Thread.currentThread().getName(), runnable);
                }
                // 计数器减一
                oneStageLatch.countDown();
                try {
                    //等待所有线程任务完成,监控是否有异常,有则统一回滚
                    twoStageLatch.await();
                    // 根据isSubmit值判断事务是否提交,可能是子线程出现异常,也有可能是子线程执行超时
                    if (isSubmit.get()) {
                        // 提交
                        transactionManager.commit(transactionStatus);
                        log.info("线程{}:事务提交成功,执行体:{}", Thread.currentThread().getName(), runnable);
                    } else {
                        // 回滚
                        transactionManager.rollback(transactionStatus);
                        log.info("线程{}:事务回滚成功,执行体:{}", Thread.currentThread().getName(), runnable);
                    }
                } catch (InterruptedException e) {
                    log.error("子线程抛出异常:{}",e.getMessage());
                }
            });
            executor.execute(thread);
        }

        //主线程担任协调者,当第一阶段所有参与者准备完成,oneStageLatch的计数为0
        //主线程发起第二阶段,执行阶段(提交或回滚)
        try {
            // 主线程等待所有线程执行完成,超时时间设置为五秒,超出等待时间则返回false,计数为0返回true
            boolean timeOutFlag = oneStageLatch.await(timeout, unit);
            long count = oneStageLatch.getCount();
            // 主线程等待超时,子线程可能发生长时间阻塞,死锁
            if (count > 0 || !timeOutFlag) {
                // 设置为回滚
                isSubmit.set(false);
                log.info("主线线程等待超时,任务即将全部回滚");
                throw new RuntimeException("主线线程等待超时,任务即将全部回滚");
            }
            twoStageLatch.countDown();
        } catch (InterruptedException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
        // 返回结果,是否执行成功,事务提交即为执行成功,事务回滚即为执行失败
        boolean flag = isSubmit.get();
        if(!flag){
            log.info("有线程发生异常,事务全部回滚");
            throw new RuntimeException("有线程发生异常,数据全部回滚");
        }else{
            log.info("主线程和子线程执行无异常,事务全部提交");
        }
        executor.shutdown();
    }

    /**
     * 初始化属性
     *
     * @param size 任务数量
     */
    private void innit(int size) {
        oneStageLatch = new CountDownLatch(size);
    }
}

 看下调用代码示例

import com.hulei.studyproject.entity.User;
import com.hulei.studyproject.threadpool.ThreadPoolUtil;
import com.hulei.studyproject.transaction.MultiThreadingTransactionManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 测试多线程事务回滚
 * @Title: UserServiceImpl
 * @Description: TODO
 * @author: hulei
 * @date: 2023/7/31 17:41
 * @Version: 1.0
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements IUserService{

    private final JdbcTemplate jdbcTemplate;

    private final PlatformTransactionManager platformTransactionManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insert() {
        System.out.println("主线程为:"+Thread.currentThread().getName());
        List<User> list = new ArrayList<>(){{
            add(new User("1","张三"));
        }};
        String sql = "insert into user(id,name) values (?,?)";
        jdbcTemplate.batchUpdate(sql,list, list.size(), (ps,d)->{
            ps.setString(1, d.getId());
            ps.setString(2,d.getName());
        });
        List<Runnable> runnableList = getRunnables();
        MultiThreadingTransactionManager multiThreadingTransactionManager = new MultiThreadingTransactionManager(platformTransactionManager,5, TimeUnit.SECONDS);
        ThreadPoolExecutor executor = ThreadPoolUtil.getThreadPool();
        multiThreadingTransactionManager.execute(runnableList,executor);
    }

    private List<Runnable> getRunnables() {
        List<Runnable> runnableList = new ArrayList<>();
        runnableList.add(()->{
            System.out.println("子线程1为:"+Thread.currentThread().getName());
            List<User> listOne = new ArrayList<>(){{
                add(new User("2","李四"));
            }};
            String sqlOne = "insert into user(id,name) values (?,?)";
            jdbcTemplate.batchUpdate(sqlOne,listOne, listOne.size(), (ps,d)->{
                ps.setString(1, d.getId());
                ps.setString(2,d.getName());
            });
            int a = 10/0;
        });
        runnableList.add(()->{
            System.out.println("子线程2为:"+Thread.currentThread().getName());
            List<User> listTwo = new ArrayList<>(){{
                add(new User("3","王五"));
            }};
            String sqlTwo = "insert into user(id,name) values (?,?)";
            jdbcTemplate.batchUpdate(sqlTwo,listTwo, listTwo.size(), (ps,d)->{
                ps.setString(1, d.getId());
                ps.setString(2,d.getName());
            });
        });
        return runnableList;
    }
}

我们在其中一个子线程处手工写了一个会抛出异常的代码

事务中使用compleablefuture,java,开发语言

 执行结果如下:

事务中使用compleablefuture,java,开发语言

 可以看到控制台报错,数据库执行结果如下:

事务中使用compleablefuture,java,开发语言

主线程和子线程数据均未生成

把异常代码注释掉

事务中使用compleablefuture,java,开发语言执行结果如下

事务中使用compleablefuture,java,开发语言

 无异常抛出,数据库结果如下:

事务中使用compleablefuture,java,开发语言

 可以看到子线程和主线程操作的数据均已回滚。

但是以上方法有一定局限性,即主线程如果再子线程执行后再抛出异常,则子线程无法回滚了,所以要求逻辑写在子线程执行之前

事务中使用compleablefuture,java,开发语言

 2处的代码必须放在方法最后写。文章来源地址https://www.toymoban.com/news/detail-815437.html

到了这里,关于CompletableFuture异步编程事务及多数据源配置详解(含gitee源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 异步编程 - 06 基于JDK中的Future实现异步编程(中)_CompletableFuture源码解析

    异步编程 - 06 基于JDK中的Future实现异步编程(中)_CompletableFuture源码解析

    CompletableFuture实现了CompletionStage接口 。 1)一个CompletionStage代表着一个异步计算节点,当另外一个CompletionStage计算节点完成后,当前CompletionStage会执行或者计算一个值;一个节点在计算终止时完成,可能反过来触发其他依赖其结果的节点开始计算。 2)一个节点(CompletionStag

    2024年02月09日
    浏览(12)
  • 并发编程 | 从Future到CompletableFuture - 简化 Java 中的异步编程

    在并发编程中,我们经常需要处理多线程的任务,这些任务往往具有依赖性,异步性,且需要在所有任务完成后获取结果。Java 8 引入了 CompletableFuture 类,它带来了一种新的编程模式,让我们能够以函数式编程的方式处理并发任务,显著提升了代码的可读性和简洁性。 在这篇

    2024年02月13日
    浏览(15)
  • 从 Future 到 CompletableFuture:简化 Java 中的异步编程

    在并发编程中,我们经常需要处理多线程的任务,这些任务往往具有依赖性,异步性,且需要在所有任务完成后获取结果。Java 8 引入了 CompletableFuture 类,它带来了一种新的编程模式,让我们能够以函数式编程的方式处理并发任务,显著提升了代码的可读性和简洁性。 在这篇

    2024年02月11日
    浏览(10)
  • CompletableFuture与线程池:Java 8中的高效异步编程搭配

    摘要:在Java 8中,CompletableFuture和线程池的结合使用为程序员提供了一种高效、灵活的异步编程解决方案。本文将深入探讨CompletableFuture和线程池结合使用的优势、原理及实际应用案例,帮助读者更好地理解并掌握这一技术。 随着多核处理器的普及,应用程序的性能和响应能

    2024年02月07日
    浏览(15)
  • Java学习笔记-day06-响应式编程Reactor与Callback、CompletableFuture三种形式异步编码对比

    Reactor 是一个基于Reactive Streams规范的响应式编程框架。它提供了一组用于构建异步、事件驱动、响应式应用程序的工具和库。Reactor 的核心是 Flux (表示一个包含零到多个元素的异步序列)和 Mono 表示一个包含零或一个元素的异步序列)。 Reactor 通过提供响应式的操作符,如

    2024年02月03日
    浏览(26)
  • 千云物流- 多数据源事务管理

    Spring只是个容器,因此它并不做任何事务的具体实现。他只是提供了事务管理的接口PlatformTransactionManager,具体内容由就由各个事务管理器来实现。 DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于S

    2024年02月02日
    浏览(13)
  • Spring Boot 多数据源及事务解决方案

    Spring Boot 多数据源及事务解决方案

    一个主库和N个应用库的数据源,并且会同时操作主库和应用库的数据,需要解决以下两个问题: 如何动态管理多个数据源以及切换? 如何保证多数据源场景下的数据一致性(事务)? 本文主要探讨这两个问题的解决方案,希望能对读者有一定的启发。 通过扩展Spring提供的抽象

    2024年02月10日
    浏览(10)
  • Spring Boot多数据源事务@DSTransactional的使用

    Spring Boot 集成com.baomidou,引入dynamic-datasource依赖,实现多数据源,这里说下事务问题: 1、一个方法中使用同一个数据源; 2、一个方法中使用了多个数据源; 这里把dao、service列出来 1、dao层   2、service层  spring boot实现多数据源:Spring Boot集成Druid实现多数据源的两种方式_涛

    2024年02月11日
    浏览(9)
  • 解决多数据源的事务问题 - 基于springboot--mybatis

    解决多数据源的事务问题 - 基于springboot--mybatis

    在Spring Boot和MyBatis中,我们有时需要在方法中同时使用两个不同的数据库,但使用 @Transactional 注解会变得复杂。这时我们可以用一种更灵活的方法来处理。 想象一下这样的场景:我们有两个数据库,我们希望在一个方法中同时操作它们,但是普通的 @Transactional 注解变得不太

    2024年02月01日
    浏览(11)
  • Spring | 基于SpringBoot的多数据源实战 - 使用seata实现多数据源的全局事务管理

    Spring | 基于SpringBoot的多数据源实战 - 使用seata实现多数据源的全局事务管理

    在软件开发中, 多数据源 的应用越来越普遍,特别是在 微服务架构 和 业务模块化 的场景下。多数据源能够让不同的业务模块和微服务拥有各自独立的数据存储,大大提高了系统的灵活性和可维护性。本文将深入探讨多数据源的配置和实施,以及在 Spring Boot 环境下,如何通

    2024年02月07日
    浏览(16)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包