配置文件
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class ThreadPoolTaskExecutorConfig {
@Bean("testPool")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(5);
// 设置最大线程数
executor.setMaxPoolSize(10);
// 设置队列容量
executor.setQueueCapacity(20);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("hello-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
模拟任务,假设每个任务大概需要耗时5S
@RestController
public class Test3Controller {
@GetMapping("/test1")
public void test1() {
test();
}
@Async("testPool")
public void test() {
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
try {
Thread.sleep(5000);
System.out.println("多线程异步执行" + Thread.currentThread().getName() + " " + format.format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
启动类加入注解
@EnableAsync
我们可以使用jmeter对接口进行并发压力测试【安装教程:https://blog.csdn.net/weixin_44464726/article/details/86152691】
当前线程池所能接受的最大任务数=最大线程数+队列容量
核心线程数=5
同时发送3个请求
结论:当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
同时发送8个请求
结论:.当线程池达到corePoolSize时,并且workQueue未满时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
如配置所示,当前所配置的拒绝策略为:CallerRunsPolicy:
意思是:若当前请求超过最大任务数时,任务将被拒绝并将被直接在execute方法的调用线程中运行被拒绝的任务。
当前最大任务数为30,我给他同时发50个请求
可以看到有10个线程在我们自己定义的线程(线程名字为hello)上运行,超出最大线程数并且超出队列的20个线程直接在exec默认线程上运行了,5秒之后,10个线程任务运行完毕,从队列中获取任务去执行,知道队列中所有的任务都已经执行完成。
四种拒绝策略:
1、CallerRunsPolicy:让主线程去运行被抛弃掉的任务。好处是能够减缓新任务的提交速度。坏处是很有可能造成当前线程也被阻塞。如果所有线程都是不能执行的,很可能导致程序没法继续跑了。
2、AbortPolicy:jdk默认策略,策略直接抛出异常,丢弃任务。
3、DiscardPolicy:不能执行的任务将被删除,类似AbortPolicy,直接丢弃但不抛异常。
4、DiscardOldestPolicy:丢弃最早的未处理完的线程任务,然后获取下一个任务执行。
可自定义拒绝策略,参考:【https://blog.csdn.net/tanglei6636/article/details/90721801】
自定义线程超时
任务:为了模拟实际程序中耗时,设置为随机睡眠时间
@Slf4j
@Component
public class Task {
private static Random random = new Random();
@Async("test1")
public Future<String> run(String a) throws InterruptedException {
long sleep = random.nextInt(4000);
Thread.sleep(sleep);
log.info("开始任务,需耗时:" + sleep + "毫秒");
//设置默认返回值
return new AsyncResult<>("test");
}
}
调用该任务,若任务没有在2秒室内返回结果就会报超时异常,然后该任务被丢弃。
@Autowired
Task task;
@GetMapping("test1")
public String test1() throws Exception {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Future<String> future = task.run("test");
//2秒之内可以拿到结果就在2秒之内返回,超过2秒就会抛异常
String s = future.get(2, TimeUnit.SECONDS);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
return s;
}