首先配置下 ThreadPoolTaskExecutor:
threadpool:
core-pool-size: 10
max-pool-size: 20
queue-capacity: 1000
keep-alive-seconds: 600
这个是放在配置文件里的参数;
接下来看配置类:
package com.guardianfreedom.fangwu.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
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;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @ClassName:ThreadConfig
* @description: 线程池配置
* @author: shen_jian
* @date: 2019-05-28 19:32
**/
@Configuration
@EnableAsync
public class ThreadConfig {
private static final Logger logger = LoggerFactory.getLogger(ThreadConfig.class);
@Value("${threadpool.core-pool-size}")
private int corePoolSize;
@Value("${threadpool.max-pool-size}")
private int maxPoolSize;
@Value("${threadpool.queue-capacity}")
private int queueCapacity;
@Value("${threadpool.keep-alive-seconds}")
private int keepAliveSeconds;
@Bean
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
// 空闲的多余线程最大存活时间
executor.setKeepAliveSeconds(keepAliveSeconds);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("async-resource-schedule-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
再接下来看如何使用:
package com.guardianfreedom.fangwu.controller;
import com.guardianfreedom.fangwu.config.ThreadConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.concurrent.CountDownLatch;
/**
* @ClassName:TempController
* @description: TODO
* @author: shen_jian
* @date: 2019-05-29 20:21
**/
@Controller
public class TempController {
private static final Logger logger = LoggerFactory.getLogger(TempController.class);
@Resource
private ThreadConfig threadConfig;
@RequestMapping(value="/multThread",method = RequestMethod.GET)
@ResponseBody
public String multThread(String index){
CountDownLatch countDownLatch = new CountDownLatch(4);
for (int xx = 0;xx<4;xx++){
threadConfig.asyncServiceExecutor().execute(new Runnable() {
@Override
public void run() {
try {
String result = "A";
logger.info("线程--> " + index + " 开始");
for (long i = 0;i < 99999L;i++){
result += "A";
}
// logger.info(result);
logger.info("线程--> " + index + " 结束");
} catch (Exception e) {
e.printStackTrace();
} finally {
Long l = countDownLatch.getCount();
// logger.info("current threads --> " + l);
countDownLatch.countDown(); // 这个不管是否异常都需要数量减,否则会被堵塞无法结束
}
}
});
}
try {
countDownLatch.await();
logger.info(index + "所有线程结束");
} catch (Exception e) {
logger.error("阻塞异常");
}
return index;
}
}
这里有个我比较好奇的地方是countDownLatch.await();这个方法他能识别出是哪次调用方法而创建的线程。比较好奇是怎么做到的。打个比方:我第一次传入参数index=1然调用方法。然后再传入参数index=2调用方法。这个时候它会在index=1的线程都结束之后打印异常所有线程结束,在index=2的线程都结束之后打印异常所有线程结束,而不是等Index=1,和Index=2的线程都结束再打印,如果是这样的话就会报错因为它不知道index=?。有大佬看到的话还希望能帮我讲解一下是为啥。
其次这里 countDownLatch.countDown(); 方法是每次将countDownLatch里的线程减一。所有创建countDownLatch的时候要将任务数(类似于我这里for循环次数)和countDownLatch线程数相等,不然不是提前执行countDownLatch.await();就是永远执行不到。。。。