一贩耐、線程和進程弧腥,線程的生命周期
二、單線程和多線程
三潮太、線程池的概念
四管搪、線程池的使用
五、多線程池配置示例
一铡买、線程和進程更鲁,線程的生命周期
鏈接:http://www.reibang.com/p/5ddcc068d177
二、單線程和多線程
單線程:只有一條線程在執(zhí)行任務
多線程:多條線程同時在執(zhí)行任務奇钞,比較常見的一種方式澡为。
多線程的安全問題:
在多線程執(zhí)行過程中,需要注意的是多線程的安全問題景埃。因為多條線程同時執(zhí)行任務媒至,可能會出現(xiàn)同時訪問同一個資源的情況顶别,導致出錯。所以需要進行同步互斥訪問處理: 使用synchronized 同步代碼塊和 Lock 鎖拒啰,只讓一個線程訪問資源驯绎,避免其他線程同時搶到CPU資源的執(zhí)行權。
三、線程池的概念
線程池概念:簡單的說,線程池是一個存放線程的容器泻云。有任務時缚甩,從線程池里頭取線程,不用的時候再放到池中給別的任務使用。
線程池使用目的:
(1)避免反復創(chuàng)建和銷毀線程帶來的時間和內存消耗
(2)線程復用,提升響應速度,當有任務的時候乞巧,線程池直接調可用的線程進行執(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ù)量是辕,即便是線程池里沒有任何任務,也會有corePoolSize個線程在等任務
(2)maximumPoolSize:線程池中可以容納的最大線程的數(shù)量(3)keepAliveTime:非核心線程可以保留的最長的空閑時間猎提。當線程池里的線程數(shù)大于corePoolSize時获三,如果等了keepAliveTime時長還沒有任務可執(zhí)行,則線程退出锨苏。
(4)unit:用來指定keepAliveTime的單位疙教,比如秒:TimeUnit.SECONDS
(5)workQueue:等待隊列,任務可以儲存在任務隊列中等待被執(zhí)行伞租,采用FIFIO原則(先進先出)
(6)threadFactory:創(chuàng)建線程的線程工廠贞谓,主要是為了給線程起名字,默認工廠的線程名字:pool-1-thread-3葵诈。
(7)handler:線程池對拒絕任務(無線程可用)的處理策略裸弦,當線程池里線程被耗盡,且隊列也滿了的時候會調用
線程池的拒絕策略包括以下4種:
ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出異常
ThreadPoolExecutor.DiscardPolicy:丟棄任務作喘,但是不拋出異常
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務理疙,然后重新提交被拒絕的任務
ThreadPoolExecutor.CallerRunsPolicy:由調用線程(提交任務的線程)處理該任務
4.2 線程池的執(zhí)行流程
step1:任務到達時,先判斷當前線程數(shù)量是否小于核心線程數(shù)corePoolSize徊都,若小于則創(chuàng)建線程來執(zhí)行任務沪斟,否則將任務放入workQueue等待隊列
step2:若workQueue等待隊列滿了,則判斷當前線程數(shù)量是否小于最大線程數(shù)maximumPoolSize暇矫,若小于則創(chuàng)建線程執(zhí)行任務主之,否則就會調用handler,線程池采用拒絕策略來處理任務李根。
五槽奕、多線程池配置示例
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注解囱持,聲明該實體類對應的配置字段前綴
(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注解盔几,開啟異步任務
(3)在實例化方法上添加 @Bean 注解
getPrimaryAsyncTaskExecutor() 與 getSecondaryAsyncTaskExecutor() 方法分別實例化了對應的 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);
// 線程池對拒絕任務(無線程可用)的處理策略
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return threadPoolTaskExecutor;
}
}
5.6 編寫異步任務的實現(xiàn)方法
(1)添加@Async注解逊拍,引用剛才定義的線程池Bean名稱
(2)添加@Component注解,保證可以組件被掃描到
注:異步任務的實現(xiàn)方法要定義在一個類中际邻,不能與調用它的方法寫在同一個類中芯丧,不然不起效果
/**
* 文件描述
* 任務異步處理
* @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 調用異步任務
/**
* 文件描述
*
* @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結果"+userInfoResult+"\nCompletableFuture耗時--》"+ (end-start)+"ms");
}
從測試結果可以看到世曾,使用了第一個線程池