记录线程池的两种实现方式
一、springboot默认的线程池@Async
个人认为,比较适合进行简单的异步调用,很方便。
@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor。默认核心线程数:8,最大线程数:Integet.MAX_VALUE,队列使用LinkedBlockingQueue,容量是:Integet.MAX_VALUE,空闲线程保留时间:60s,线程池拒绝策略:AbortPolicy。
第一步 创建AsyncConfig配置类
代码片
.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* 此类使定时任务多线程异步执行
* 在定时任务的类或者方法上添加@Async ,可使每一个任务都是在不同的线程中
* @author sun
*/
@Configuration
@EnableAsync
public class AsyncConfig {
/**
* 此处成员变量应该使用@Value从配置中读取
*/
private int corePoolSize = 30;
private int maxPoolSize = 50;
private int queueCapacity = 10;
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
// 队列大小
executor.setQueueCapacity(queueCapacity);
executor.initialize();
return executor;
}
}
@Configuration注解,服务启动进行扫描,注册Bean(taskExecutor)
@EnableAsync注解,可以异步执行,差不多是开启多线程的意思
第二步 在需要异步执行的方法上加上@Async注解
代码片
.
/**
* 异步执行
*
* @param updater 1
*/
@Async("taskExecutor")
public synchronized void notify(OrderUpdater updater) {
if (observers.isEmpty()) {
return;
}
for (OrderObserver observer : observers) {
try {
observer.doSomeThing(updater);
} catch (Exception e) {
logger.error("通知失败", e);
}
}
}
可以在@Async注解上指定异步线程池Bean的名字(“taskExecutor”)
注意:需要异步调用的方法notify与调用者不能在同一个class里面,否则异步方法不会生效!
二、使用ThreadPoolExecutor
第一步 创建ThreadPoolManager类配置单例模式线程池
代码片
.
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 单例模式线程池
*
* @author sun
*/
@Slf4j
public class ThreadPoolManager {
/**
* 针对数据量大的结果进行多线程处理,最大线程数
* <p>
* Nthreads=CPU数量
* Ucpu=目标CPU的使用率,0<=Ucpu<=1
* W/C=任务等待时间与任务计算时间的比率
* <p>
* Nthreads =Ncpu*Ucpu*(1+W/C)
* 针对数据量大的结果进行多线程处理,固定线程数
*/
private static final int THREAD_NUM = 80;
private static final int MAX_THREAD_NUM = 150;
private ExecutorService executorService;
private int executeThreadNum = 1;
private ThreadPoolManager() {
if (executorService == null) {
// AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作;
// CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;
// DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;
// DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;
executorService = new ThreadPoolExecutor(THREAD_NUM, MAX_THREAD_NUM, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100),
r -> {
//线程命名
return new Thread(r, "threadPool" + r.hashCode());
}, new ThreadPoolExecutor.CallerRunsPolicy());
}
}
private static ThreadPoolManager instancce;
public static ThreadPoolManager getInstancce() {
if (instancce == null) {
instancce = new ThreadPoolManager();
}
return instancce;
}
public void executeTask(Runnable runnable) {
executorService.execute(runnable);
log.debug("异步线程执行了{}次", executeThreadNum++);
}
}
第二步 使用线程池
代码片
.
for (int i = 0; i < loopNum; i++) {
ThreadPoolManager.getInstancce().executeTask(() -> {
try {
// 让线程阻塞,使后续任务进入缓存队列
Thread.sleep(1000);
// do something
} catch (Exception e) {
e.printStackTrace();
}
});
}