一、線程和進程,線程的生命周期
二、單線程和多線程
三、線程池的概念
四、線程池的使用
五、多線程池配置示例
一、線程和進程,線程的生命周期
鏈接:http://www.lxweimin.com/p/5ddcc068d177
二、單線程和多線程
單線程:只有一條線程在執(zhí)行任務(wù)
多線程:多條線程同時在執(zhí)行任務(wù),比較常見的一種方式。
多線程的安全問題:
在多線程執(zhí)行過程中,需要注意的是多線程的安全問題。因為多條線程同時執(zhí)行任務(wù),可能會出現(xiàn)同時訪問同一個資源的情況,導致出錯。所以需要進行同步互斥訪問處理: 使用synchronized 同步代碼塊和 Lock 鎖,只讓一個線程訪問資源,避免其他線程同時搶到CPU資源的執(zhí)行權(quán)。
三、線程池的概念
線程池概念:簡單的說,線程池是一個存放線程的容器。有任務(wù)時,從線程池里頭取線程,不用的時候再放到池中給別的任務(wù)使用。
線程池使用目的:
(1)避免反復(fù)創(chuàng)建和銷毀線程帶來的時間和內(nèi)存消耗
(2)線程復(fù)用,提升響應(yīng)速度,當有任務(wù)的時候,線程池直接調(diào)可用的線程進行執(zhí)行,不需要等待線程的創(chuàng)建
(3)對線程進行統(tǒng)一的分配,提高線程的管理性
四、線程池的使用
4.1 線程池的創(chuàng)建
java中創(chuàng)建線程池的一個類:Executor,通常使用它的一個子類:ThreadPoolExecutor。
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
線程池這幾個參數(shù)的解釋:
(1)corePoolSize:線程池中的核心線程數(shù)量,即便是線程池里沒有任何任務(wù),也會有corePoolSize個線程在等任務(wù)
(2)maximumPoolSize:線程池中可以容納的最大線程的數(shù)量(3)keepAliveTime:非核心線程可以保留的最長的空閑時間。當線程池里的線程數(shù)大于corePoolSize時,如果等了keepAliveTime時長還沒有任務(wù)可執(zhí)行,則線程退出。
(4)unit:用來指定keepAliveTime的單位,比如秒:TimeUnit.SECONDS
(5)workQueue:等待隊列,任務(wù)可以儲存在任務(wù)隊列中等待被執(zhí)行,采用FIFIO原則(先進先出)
(6)threadFactory:創(chuàng)建線程的線程工廠,主要是為了給線程起名字,默認工廠的線程名字:pool-1-thread-3。
(7)handler:線程池對拒絕任務(wù)(無線程可用)的處理策略,當線程池里線程被耗盡,且隊列也滿了的時候會調(diào)用
線程池的拒絕策略包括以下4種:
ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出異常
ThreadPoolExecutor.DiscardPolicy:丟棄任務(wù),但是不拋出異常
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù),然后重新提交被拒絕的任務(wù)
ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程(提交任務(wù)的線程)處理該任務(wù)
4.2 線程池的執(zhí)行流程
step1:任務(wù)到達時,先判斷當前線程數(shù)量是否小于核心線程數(shù)corePoolSize,若小于則創(chuàng)建線程來執(zhí)行任務(wù),否則將任務(wù)放入workQueue等待隊列
step2:若workQueue等待隊列滿了,則判斷當前線程數(shù)量是否小于最大線程數(shù)maximumPoolSize,若小于則創(chuàng)建線程執(zhí)行任務(wù),否則就會調(diào)用handler,線程池采用拒絕策略來處理任務(wù)。
五、多線程池配置示例
5.1 添加依賴
該依賴的作用是用于配置類和實體類的字段定位
<!--用于配置類和實體類的字段定位-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
5.2 定義基類,定義線程池基本參數(shù)
/**
* 文件描述
* 線程池基本參數(shù)
* @author hjj
* @date 2020年08月19日 15:43
*/
@Data
public class AsyncContants {
/**
* 核心線程數(shù)
*/
private Integer corePoolSize=10;
/**
* 最大線程數(shù)
*/
private Integer maxPoolSize=20;
/**
* 等待隊列
*/
private Integer queueCapacity=50;
/**
*線程池維護線程所允許的空閑時間,單位為秒
*/
private Integer keepAliveSeconds=120;
}
5.3 配置文件中配置線程池基本參數(shù)值
#線程池配置
#第一個線程池
primary.async.corePoolSize=10
primary.async.maxPoolSize=20
primary.async.queueCapacity=50
primary.async.keepAliveSeconds=120
#第二個線程池
secondary.async.corePoolSize=20
secondary.async.maxPoolSize=40
secondary.async.queueCapacity=100
secondary.async.keepAliveSeconds=120
5.4 定義多個線程池配置類
讀取配置文件,進行線程池配置
(1)添加@Component注解,聲明該類為 Spring 組件,交由容器管理
(2)添加@ConfigurationProperties注解,聲明該實體類對應(yīng)的配置字段前綴
(3)繼承AsyncContants基類
/**
* 文件描述
* 讀取配置文件,進行線程池配置
* @author hjj
* @date 2020年08月19日 15:49
*/
@Component
@ConfigurationProperties(value = "primary.async")
public class PrimaryAsyncContants extends AsyncContants{
}
/**
* 文件描述
* 讀取配置文件,進行線程池配置
* @author hjj
* @date 2020年08月19日 15:50
*/
@Component
@ConfigurationProperties(value = "secondary.async")
public class SecondaryAsyncConstants extends AsyncContants{
}
5.5線程池的自定義配置
將配置信息注入到線程池對象 ThreadPoolTaskExecutor 中,生成可用的 Executor 對象
(1)添加@Configuration注解
(2)添加@EnableAsync注解,開啟異步任務(wù)
(3)在實例化方法上添加 @Bean 注解
getPrimaryAsyncTaskExecutor() 與 getSecondaryAsyncTaskExecutor() 方法分別實例化了對應(yīng)的 Executor 對象,并通過注解 @Bean 將其納入容器中
后續(xù)需要使用線程池,直接引用Bean名稱
/**
* 文件描述
* 多個線程池自定義配置
* @author hjj
* @date 2020年08月19日 15:56
*/
@Configuration
@EnableAsync
public class AsyncTaskPoolConfig {
private PrimaryAsyncContants primaryAsyncContants;
private SecondaryAsyncConstants secondaryAsyncConstants;
@Autowired(required = false)
public AsyncTaskPoolConfig(PrimaryAsyncContants primaryAsyncContants, SecondaryAsyncConstants secondaryAsyncConstants){
this.primaryAsyncContants = primaryAsyncContants;
this.secondaryAsyncConstants = secondaryAsyncConstants;
}
@Bean(name = "primaryAsyncTaskExecutor")
public Executor getPrimaryAsyncTaskExecutor(){
return initExecutor(primaryAsyncContants,"primaryAsyncTaskExecutor-");
}
@Bean(name = "secondaryAsyncTaskExecutor")
public Executor getSecondaryAsyncTaskExecutor(){
return initExecutor(secondaryAsyncConstants,"secondaryAsyncTaskExecutor-");
}
private ThreadPoolTaskExecutor initExecutor(AsyncContants asyncContants,String prefix){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(asyncContants.getCorePoolSize());
threadPoolTaskExecutor.setMaxPoolSize(asyncContants.getMaxPoolSize());
threadPoolTaskExecutor.setQueueCapacity(asyncContants.getQueueCapacity());
threadPoolTaskExecutor.setKeepAliveSeconds(asyncContants.getKeepAliveSeconds());
threadPoolTaskExecutor.setThreadNamePrefix(prefix);
// 線程池對拒絕任務(wù)(無線程可用)的處理策略
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return threadPoolTaskExecutor;
}
}
5.6 編寫異步任務(wù)的實現(xiàn)方法
(1)添加@Async注解,引用剛才定義的線程池Bean名稱
(2)添加@Component注解,保證可以組件被掃描到
注:異步任務(wù)的實現(xiàn)方法要定義在一個類中,不能與調(diào)用它的方法寫在同一個類中,不然不起效果
/**
* 文件描述
* 任務(wù)異步處理
* @author hjj
* @date 2020年07月22日 16:33
*/
@Component
@Slf4j
public class AsyncTask {
@Async("primaryAsyncTaskExecutor")
public CompletableFuture<String> getUserInfoByCompletableFuture(String userName) {
String userInfo="";
try{
Thread.sleep(10);
userInfo = userName+"的基本信息!";
log.info("線程:"+Thread.currentThread().getName());
} catch (InterruptedException e) {
log.error(e.getMessage(),e);
e.printStackTrace();
}
return CompletableFuture.completedFuture(userInfo);
}
}
5.7 調(diào)用異步任務(wù)
/**
* 文件描述
*
* @author hjj
* @date 2020年07月22日 16:48
*/
@Component
@Slf4j
public class CompletableFutureDemo {
@Autowired
private AsyncTask asyncTask;
List<String> batchGetUserInfoByCompletableFuture(List<String> userNameList) throws InterruptedException, ExecutionException{
List<CompletableFuture<String>> userInfoFutrues = userNameList.stream().map(userName->asyncTask.getUserInfoByCompletableFuture(userName)).collect(Collectors.toList());
return userInfoFutrues.stream().map(CompletableFuture::join).collect(Collectors.toList());
}
}
5.8 測試
@Test
public void CompletableFutureTest()throws InterruptedException, ExecutionException {
List<String> userNameList = new ArrayList<>();
for(int i=0;i<5;i++){
userNameList.add("Ada"+i);
}
long start =System.currentTimeMillis();
List<String> userInfoResult = completableFutureDemo.batchGetUserInfoByCompletableFuture(userNameList);
long end =System.currentTimeMillis();
log.info("CompletableFuture結(jié)果"+userInfoResult+"\nCompletableFuture耗時--》"+ (end-start)+"ms");
}
從測試結(jié)果可以看到,使用了第一個線程池