首先配置下 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();就是永遠執行不到。。。。