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