java 利用 CompletableFuture 解决异步编程子线程,主线程事务不一致问题

思路 : 使用 CompletableFuture.supplyAsync 异步执行调取另一个service方法 并获取返回结果

主线程等到结果后进行比对 (一般是看是否成功执行 )

结果错误进行主线程异常抛出 配合@Transactional

可解决大部分常用场景

package com.eco.user.service;

import com.eco.common.result.Result;
import com.eco.user.entity.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;


@Service
public class ThirdService {
    @Autowired
    ISysUserService sysUserService;
    @Autowired
    @Qualifier("asyncExecutor")
    Executor executor;


    @Transactional
    public Result<String> npc() throws ExecutionException, InterruptedException {

        SysUser sysUser = new SysUser();
        sysUser.setId(18L);
        sysUser.setStatus(1);
        sysUserService.updateById(sysUser);
        List<CompletableFuture<Boolean>> futures = new ArrayList<>();

        CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
            try {
                sysUserService.listByParam();
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("出错了");
                return false;
            }
            return true;
        },executor);

// 多任务编排使用
// 使用allOf方法来表示所有的并行任务
//        futures.add(future);
//        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
//                futures.toArray(new CompletableFuture[futures.size()]));
//
//        // 下面的方法可以帮助我们获得所有子任务的处理结果
//        CompletableFuture<List<Boolean>> finalResults = allFutures.thenApply(v ->
//                futures.stream().map(CompletableFuture::join).collect(Collectors.toList())
//        );
//        List<Boolean> resultList =  finalResults.join();

        System.out.println(future.get());
        if (future.get().equals(false)) {
            throw new RuntimeException("结果错误");
        }

        return Result.success("fff");
    }
}

调用另一个service

package com.eco.user.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eco.user.entity.SysPermission;
import com.eco.user.entity.SysUser;
import com.eco.user.mapper.SysUserMapper;
import com.eco.user.service.ISysPermissionService;
import com.eco.user.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.UUID;


@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    @Autowired
    ISysPermissionService sysPermissionService;


    @Override
    @Transactional
    public SysUser listByParam() {
        SysUser sysUser = new SysUser();
        sysUser.setId(UUID.randomUUID().getMostSignificantBits());
        sysUser.setStatus(0);
        save(sysUser);
        System.out.println("ddd");
        SysPermission sysPermission = new SysPermission();
        sysPermission.setId(UUID.randomUUID().getMostSignificantBits());
        sysPermission.setCreateTime(new Date());
        int t = 1 / 0;
        sysPermissionService.save(sysPermission);
        return sysUser;
    }

}

如果不需要等待线程返回结果 直接在方法添加@Async 配合 @Transactional 即可保证子线程方法内部的事务一致

自定义异步线程池

package com.eco.common.web.config;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

import com.eco.common.web.properties.TaskThreadPoolConfig;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import lombok.extern.slf4j.Slf4j;

/**
 * 自定义异步线程池
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(TaskThreadPoolConfig.class)
public class AsyncPoolConfig implements AsyncConfigurer {


    @Autowired
    private TaskThreadPoolConfig config;


    @Bean("asyncExecutor")
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(config.getCorePoolSize());
        executor.setMaxPoolSize(config.getMaxPoolSize());
        executor.setQueueCapacity(config.getQueueCapacity());
        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
        executor.setThreadNamePrefix("eco_async_");

        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();

        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

        return new AsyncExceptionHandler();
    }

    /**
     *  使用@Async 注解异常处理
     */
    @SuppressWarnings("all")
    class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            // 1、打印异常堆栈
            throwable.printStackTrace();
            // 2、日志记录错误信息
            log.error("AsyncError:{}, Method:{}, Param:{}", throwable.getMessage(), method.getName(), Arrays.asList(objects));
            // 3、TODO 发生异常后通知管理人员(邮件,短信)进一步处理
        }
    }
}
package com.eco.common.web.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;


@Data
@ConfigurationProperties("task.pool")
public class TaskThreadPoolConfig {

    /**
     * 核心线程池大小
     */
    private int corePoolSize =1;

    /**
     * 最大线程数
     */
    private int maxPoolSize =5;

    /**
     * 队列容量
     */
    private int queueCapacity =1;

    /**
     * 活跃时间
     */
    private int keepAliveSeconds = 5;

}

备注: 本方法缺陷   如果多个子线程执行不同方法  无法对子线程所有方法进行事务统一处理

考虑从数据库层面对事务做统一处理

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值