springboot多线程任务执行TaskExecutor的使用,以及使用@Async实现异步调用:自定义线程池

1.@Async实现异步调用

1.1pom.xml

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

1.2.启动类

@EnableAsync
@SpringBootApplication
public class LearnutilsApplication {

   public static void main(String[] args) {
      SpringApplication.run(LearnutilsApplication.class, args);
   }

   /**
    * 核心线程数10:线程池创建时初始化的线程数
    * 最大线程数20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
    * 缓冲队列200:用来缓冲执行任务的队列
    * 允许线程的空闲时间60秒:超过了核心线程数之外的线程,在空闲时间到达之后会被销毁
    * 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
    * 线程池对拒绝任务的处理策略:此处采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在execute方法的调用线程中运行被拒绝的任务;如果执行程序已被关闭,则会丢弃该任务
    * 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
    * 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
    */
   @EnableAsync
   @Configuration
   class TaskPoolConfig{
      @Bean("taskExecutor")
      public Executor taskExecutor(){
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
         executor.setCorePoolSize(10);
         executor.setMaxPoolSize(20);
         executor.setQueueCapacity(200);
         executor.setKeepAliveSeconds(60);
         executor.setThreadNamePrefix("TaskExecutor-");
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
         executor.setWaitForTasksToCompleteOnShutdown(true);
         executor.setAwaitTerminationSeconds(60);
         return executor;
      }
   }

}

1.3定义controller

@RequestMapping(value = "/AsyncController")
@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @Autowired
    private AsyncService2 asyncService2;

    @Autowired
    private AsyncService3 asyncService3;

    @GetMapping(value = "/sendSms")
    public String sendSms() throws Exception{
       
        Future<String> sms = asyncService.sendSms();
        Future<String> sms2 = asyncService2.sendSms();
        Future<String> sms3 = asyncService3.sendSms();
        int i = 0;
        for (;;) {
            //如果都执行完就跳出循环,isDone方法,如果此线程执行完,true
            if (sms.isDone() && sms2.isDone() && sms3.isDone()) {
                break;
            }
        }
        //get是获取结果集
        return sms.get()+sms2.get()+sms3.get();
    }
}

1.4定义接口

public interface AsyncService {
    Future<String> sendSms();
}

1.5实现类

@Service
public class AsyncServiceImpl implements AsyncService {
    //Future<String> 返回结果 AsyncResult<String>
    @Async("taskExecutor")
    @Override
    public Future<String> sendSms() {
        return new AsyncResult<>("000000");
    }
}

1.6将isDone换程CountDownLatch来判断线程是否执行完实例化CountDownLatch并且制定线程个数,线程个数就是从本地异步调用的方法个输,并且传入线程任务中,每个线程执行完毕就调用countDown()方法。最后在调用await()方法。这样在线程计数为零之前,线程就会一直等待。

AsyncResult用来封装结果集,否则结果集无法返回

@GetMapping(value = "/sendSms2")
public String sendSms2() throws Exception{
    CountDownLatch downLatch = new CountDownLatch(3);
    Future<String> s = asyncService4.sendSms(downLatch);
    Future<String> s1 = asyncService5.sendSms(downLatch);
    Future<String> s2 = asyncService6.sendSms(downLatch);
    downLatch.await();
    return s.get()+s1.get()+s2.get();
}

1.7接口,将CountDownLatch传给方法

public interface AsyncService4 {
    Future<String> sendSms(CountDownLatch downLatch);
}

1.8方法

@Service
public class AsyncService4Impl implements AsyncService4 {

    @Async("taskExecutor")
    @Override
    public Future<String> sendSms(CountDownLatch downLatch) {
        downLatch.countDown();
        return new AsyncResult<>("11111");
    }
}

2、TaskExecutor的使用

 2.1注册TaskExecutor

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author yanjun
 * @date 2019/8/1 16:04
 **/
@Configuration
public class MainConfiguration {
    @Bean
    public TaskExecutor getTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(5);
        // 设置最大线程数
        executor.setMaxPoolSize(10);
        // 设置队列容量
        executor.setQueueCapacity(20);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("post-lending-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

2.2 使用TaskExecutor

@Autowired
private TaskExecutor taskExecutor;

public ResultVO findHandlingRecordByAssociationId(Integer associationId) throws InterruptedException{
    Map<String, Object> map = new HashMap<>(2);
   //线程计数器(等待所有线程执行完统一返回)
    CountDownLatch countDownLatch = new CountDownLatch(10);
    taskExecutor.execute(() -> {
        try {
            //service调用
            map.put("HandlingRecord", legalLitigationService.findHandlingRecordByAssociationId(associationId));
        }finally {
            countDownLatch.countDown();
        }
    });
    taskExecutor.execute(() -> {
        try {
            map.put("CaseBasic", legalLitigationService.findCaseDetailsById(associationId));
        }finally {
            countDownLatch.countDown();
        }
    });
    countDownLatch.await();
    return ResultVO.putSuccess(map);
}

2.3 子线程依赖主线程操作DB的结果,子线程在主线程执行完成之后执行

public static void main(String[] args) {
    CountDownLatch countdownlatch = new CountDownLatch(1);
    taskExecutor.execute(() -> {
        try {
            countdownlatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("子线程执行完毕");
    });

    //让主线城程睡一会
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("主线程执行完毕");
    countdownlatch.countDown();
}

2.4 场景:在主线程A中操作DB保存记录,然后再起一个子线程流程B,子线程B操作数据后需要更新A保存的记录,在子线程B中去查询主线程A存的数据,如果主线程A的事务可能还没有提交,此时子线程B可能会查询不到存的新数据。

解决方案:主线程A事务提交后再调用子线程B,确保子线程B查询数据的时候主线程A已经操作DB完成

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * 场景:在主线程A中操作DB保存记录,然后再起一个子线程流程B,子线程B操作数据后需要更新A保存的记录,在子线程B中去查询主线程A存的数据,如果主线程A的事务可能还没有提交,此时子线程B可能会查询不到存的新数据。
 * @author 小书生008
 */
@Component
@Slf4j
public class AfterCommitExecutor extends TransactionSynchronizationAdapter implements Executor {
    private static final ThreadLocal<List<Runnable>> RUNNABLES = new ThreadLocal<>();

    @Resource
    private TaskExecutor taskExecutor;

    @Override
    public void execute(Runnable runnable) {
        log.info("Submitting new runnable {} to run after commit", runnable);
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            log.info("Transaction synchronization is NOT ACTIVE. Executing right now runnable {}", runnable);
            runnable.run();
            return;
        }
        List<Runnable> threadRunnables = RUNNABLES.get();
        if (threadRunnables == null) {
            threadRunnables = new ArrayList<>();
            RUNNABLES.set(threadRunnables);
            TransactionSynchronizationManager.registerSynchronization(this);
        }
        threadRunnables.add(runnable);
    }

    @Override
    public void afterCommit() {
        List<Runnable> threadRunnables = RUNNABLES.get();
        log.info("Transaction successfully committed, executing {} runnable", threadRunnables.size());
        for (int i = 0; i < threadRunnables.size(); i++) {
            Runnable runnable = threadRunnables.get(i);
            log.info("Executing runnable {}", runnable);
            try {
                taskExecutor.execute(runnable);
            } catch (RuntimeException e) {
                log.error("Failed to execute runnable " + runnable, e);
            }
        }
    }

    @Override
    public void afterCompletion(int status) {
        log.info("Transaction completed with status {}", status == STATUS_COMMITTED ? "COMMITTED" : "ROLLED_BACK");
        RUNNABLES.remove();
    }

}

2.5使用2.4的方法

@Resource
private AfterCommitExecutor afterCommitExecutor;
@Transactional(rollbackFor = Exception.class)
@Override
public void addCustomerBank() {
    CustomerBank build = CustomerBank.builder()
            .bankName("1111111111")
            .accountNumber("111111111111")
            .customerName("12345")
            .build();
    customerBankMapper.insert(build);
    afterCommitExecutor.execute(() ->{
        try {
            build.setAccountNumber("222222222");
            customerBankMapper.updateByPrimaryKeySelective(build);
        } catch (Exception e) {
            e.printStackTrace();
            customerBankMapper.delete(build);
        }
    });
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

《小书生》

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值