Spring AOP 異步操作實現
異步場景分析
在開發系統的過程中,通常會考慮到系統的性能問題,提升系統性能的一個重要思想就是“串行”改“並行”。
假如我們在一個線程中進行操作多個業務的時候,這樣的話線程就會不停的調度,假如有一個業務中途被掛起啦,那么其他的業務也只能等待,知道被掛起的業務重新獲得資源,可以運行.這樣的話就給客戶帶來了卡頓的不好體驗,當然我們可以在執行的業務上手動 new Thread,開啟一個線程,但是這樣的話,每次訪問都會創建新的線程,如果是高並發的情況下,就會造成內存溢出,系統崩潰.為了更好的解決這個問題,spring 中提供了異步注解@Async
Spring 業務的異步實現
啟動異步配置
在基於注解方式的配置中,借助@EnableAsync注解進行異步啟動聲明,Spring Boot版的項目中,將@EnableAsync注解應用到啟動類上
示例代碼:
package com.cy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync//spring容器啟動時會創建線程池
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Spring中@Async注解應用
在我們需要執行的業務方法上使用@Async注解進行異步聲明
@Async //使用此注解的描述的方法會運行在由spring框架提供的一個線程中
@Override
public void saveObject(SysLog sysLog) {
sysLogDao.insertObject(sysLog);
}
如果需要獲取業務層異步方法的執行結果,那么返回值必須通過Future<T>
進行接收,返回的時候使用new AsyncResult<T>
實例代碼:
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Async
@Override
public Future<Integer> saveObject(SysLog entity) {
int rows=sysLogDao.insertObject(entity);
//try{Thread.sleep(5000);}catch(Exception e) {}
return new AsyncResult<Integer>(rows);
}
//,AsyncResult對象可以對異步方法的執行結果進行封裝,假如外界需要異步方法結果時,可以通過Future對象的get方法獲取結果。
說明:對於@Async注解默認會基於ThreadPoolTaskExecutor對象獲取工作線程,然后調用由@Async描述的方法,讓方法運行於一個工作線程,以實現異步操作。但是假如系統中的默認拒絕處理策略,任務執行過程的異常處理不能滿足我們自身業務需求的話,我可以對異步線程池進行自定義.(SpringBoot中默認的異步配置可以參考自動配置對象TaskExecutionAutoConfiguration).
spring框架連接池簡易配置
task:
execution:
pool:
core-size: 10 #核心線程數,當池中線程數沒達到core-size時,每來一個請求都創建一個新的線程
queue-capacity: 256 #隊列容量,當核心線程都在忙,再來新的任務,會將任務放到隊列
max-size: 128 #當核心線程都在忙,隊列也滿了,再來新的任務,此時會創建新的線程,直到達到maxSize
keep-alive: 60 #當任務高峰過后,有些線程會空閑下來,這空閑現線程達到一定的時間會被釋放。
allow-core-thread-timeout: false
thread-name-prefix: service-task-
對於線程池配置參數的含義,可以通過ThreadPoolExecutor對象進行分析
ThreadPoolExecutor
ThreadPoolExecutor提供了四個構造函數,最后都會歸結於下面這個構造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
參數的意義如下:
- corePoolSize:該線程池中核心線程數最大值
- maximumPoolSize: 該線程池中線程總數最大值
- keepAliveTime:該線程池中非核心線程閑置超時時長
- unit:keepAliveTime的單位
- workQueue:阻塞隊列BlockingQueue,維護着等待執行的Runnable對象
- threadFactory:創建線程的接口,需要實現他的Thread newThread(Runnable r)方法。
- RejectedExecutionHandler:飽和策略,最大線程和工作隊列容量且已經飽和時execute方法都將調用RejectedExecutionHandler 。
大概的流程圖:
大致的流程描述為:
- 向線程池中添加任務,當任務數量少於corePoolSize時,會自動創建thead來處理這些任務;
- 當添加任務數大於corePoolSize且少於maximmPoolSize時,不在創建線程,而是將這些任務放到阻塞隊列中,等待被執行;
- 接上面2的條件,且當阻塞隊列滿了之后,繼續創建thread,從而加速處理阻塞隊列;
- 當添加任務大於maximmPoolSize時,根據飽和策略決定是否容許繼續向線程池中添加任務,默認的飽和策略是AbortPolicy(直接丟棄)
線程池中使用的阻塞隊列
ArrayBlockingQueue:基於數組結構的有界阻塞隊列,構造函數一定要傳大小,FIFO(先進先出);
LinkedBlockingQueue:無界,默認大小65536(Integer.MAX_VALUE),當大量請求任務時,容易造成內存耗盡。
SynchronousQueue:同步隊列,是一個特殊的BlockingQueue,它沒有容量(這是因為在SynchronousQueue中,插入將等待另一個線程的刪除操作,反之亦然)。具體可以參考:《Java SynchronousQueue Examples(譯)》
PriorityBlockingQueue: 優先隊列,無界。
DelayedWorkQueue:這個隊列接收到任務時,首先先入隊,只有達到了指定的延時時間,才會執行任務
阻塞隊列常見的方法
常見四種線程池
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadExecutor
- newScheduledThreadPool
它們通過Executors以靜態方法的方式直接調用,實質上是它們最終調用的是ThreadPoolExecutor的構造方法
轉載自:https://baijiahao.baidu.com/s?id=1638752286286006421&wfr=spider&for=pc