单线程池 多线程池在springboot中的使用以及接受线程返回值Future
- 环境:springboot
单线程池
-
需求场景:数据库a表中有
version
字段,作用于乐观锁,但是乐观锁是并发环境下version
字段不一致时会发生回滚。起初想用synchronized
进行线程守护,或者最直接用悲观锁
就可以了,甚至乐观锁
事务回滚再执行,想到学过单线程池看能否解决,牺牲时间,确保并发下按顺序执行a表CUD(insert、update、delete操作)。 -
示意图:
-
config - java配置类
@Configuration
public class ThreadPoolConfig {
// 单线程池
@Bean("syncServiceExecutor")
public ExecutorService syncServiceExecutor() {
// 设定了线程前缀
return Executors.newSingleThreadExecutor(ThreadFactoryBuilder.create().setNamePrefix("sync-service-").build());
}
}
- service
Future
为了返回结果, 详细信息查看jdk
文档
@Async("syncServiceExecutor")
@Override
public Future<Result> t() {
log.info("{} start...", Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (Exception e) { e.printStackTrace(); }
log.info("{} end...", Thread.currentThread().getName());
return new AsyncResult<>(Result.success(Thread.currentThread().getName()));
}
- controller
? Future get():进行同步堵塞获取service返回的Result结果
@GetMapping("/t")
public Result t() throws Exception {
return registerService.t().get(5, TimeUnit.SECONDS); // 5秒内接受到返回值, 否则抛出异常
}
- test
- 环境:postman 批量执行5+个,配合浏览器执行高并发
- 环境:postman 批量执行5+个,配合浏览器执行高并发
经过1秒多获取结果,再来确认是否是一个一个用户执行的。
由线程名一直都是保持0的可以确认并发下确实是一个一个执行的
- 看看Executors.newSingleThreadExecutor()方法的源码
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
了解的
ThreadPoolExecutor
肯定知道就是多线程池控制了线程池的数量等…
多线程池
- yaml
my:
thread-pool:
core-pool-size: 10 # 线程池维护线程的最少数量
max-pool-size: 50 # 线程池维护线程的最大数量
queue-capacity: 100 # 缓存队列数量
threadNamePrefix: async-service- # 多线程前缀
keep-alive: 60 # 允许的空闲时间
- 属性类
@Component
@ConfigurationProperties("my.thread-pool")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ThreadPoolConfigProperties {
private Integer corePoolSize;
private Integer maxPoolSize;
private Integer queueCapacity;
private String threadNamePrefix;
private Integer keepAlive;
}
- config配置类
@Configuration
@ConditionalOnBean(ThreadPoolConfigProperties.class)
public class ThreadPoolConfig {
@Autowired
private ThreadPoolConfigProperties threadPoolConfigProperties;
// 单线程池
@Bean("syncServiceExecutor")
public ExecutorService syncServiceExecutor() {
// 设定了线程前缀
return Executors.newSingleThreadExecutor(ThreadFactoryBuilder.create().setNamePrefix(threadPoolConfigProperties.getThreadNamePrefix()).build());
}
// 多线程池
@Bean(name = "asyncServiceExecutor")
public AsyncTaskExecutor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(threadPoolConfigProperties.getCorePoolSize());
executor.setMaxPoolSize(threadPoolConfigProperties.getMaxPoolSize());
executor.setKeepAliveSeconds(threadPoolConfigProperties.getKeepAlive());
executor.setQueueCapacity(threadPoolConfigProperties.getQueueCapacity());
executor.setThreadNamePrefix(threadPoolConfigProperties.getThreadNamePrefix());
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
//对拒绝task的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
- service
@Async("asyncServiceExecutor")
@Override
public Future<Result> t() {
log.info("{} start...", Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (Exception e) { e.printStackTrace(); }
log.info("{} end...", Thread.currentThread().getName());
return new AsyncResult<>(Result.success(Thread.currentThread().getName()));
}
- controller
@GetMapping("/t")
public Result t() throws Exception {
return registerService.t().get(5, TimeUnit.SECONDS);
}
- test
- 环境:postman 批量执行5+个,配合浏览器执行高并发
检测当前线程使用情况的定时器
- info bean
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyThreadInfo {
private Integer activeCount; //存活数量
private Integer corePoolSize; //线程池基本大小
private Integer maxPoolSize; //最大数量
private Integer poolSize; //当前线程池大小
private Integer availableQueueLength; // 可用线程长度
private Integer queueSize; // 线程长度
private String serverIp; // 服务器IP
}
- 定时器
@Component
@Slf4j
@EnableScheduling
public class ThreadTaskQuart {
@Autowired
private AsyncTaskExecutor asyncServiceExecutor;
@Scheduled(cron = "0/5 * * * * *")
public void getThreadInfo(){
InetAddress inte = null;
try{
inte = InetAddress.getLocalHost();
}catch(Exception e){
e.printStackTrace();
}
MyThreadInfo info = new MyThreadInfo();
ThreadPoolTaskExecutor asyncServiceExecutor = (ThreadPoolTaskExecutor) this.asyncServiceExecutor;
info.setActiveCount(asyncServiceExecutor.getActiveCount());
info.setCorePoolSize(asyncServiceExecutor.getCorePoolSize());
info.setMaxPoolSize(asyncServiceExecutor.getMaxPoolSize());
info.setPoolSize(asyncServiceExecutor.getPoolSize());
ThreadPoolExecutor threadPoolExecutor = asyncServiceExecutor.getThreadPoolExecutor();
info.setAvailableQueueLength(threadPoolExecutor.getQueue().remainingCapacity());
info.setQueueSize(threadPoolExecutor.getQueue().size());
info.setServerIp(inte.getHostAddress());
log.info("线程信息: {}", JSONObject.toJSONString(info));
}
}