日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

提高系統性能的必備技能:異步任務完全指南

來源: 責編: 時間:2023-10-28 16:30:43 366觀看
導讀環境:Spring5.3.23本文將介紹Spring框架中的異步任務,闡述為什么要使用異步任務以及異步任務帶來的好處。通過對Spring異步任務的深入了解,我們將掌握如何在Spring應用程序中實現高效的異步處理,并利用異步任務提高應用程

環境:Spring5.3.23QJk28資訊網——每日最新資訊28at.com

本文將介紹Spring框架中的異步任務,闡述為什么要使用異步任務以及異步任務帶來的好處。通過對Spring異步任務的深入了解,我們將掌握如何在Spring應用程序中實現高效的異步處理,并利用異步任務提高應用程序的性能和響應能力。QJk28資訊網——每日最新資訊28at.com

1. 前言

為什么要使用異步任務?QJk28資訊網——每日最新資訊28at.com

在傳統的同步應用程序中,每個請求都需要等待處理完成后再返回結果。這種方式在處理耗時操作時會導致應用程序性能下降,響應時間增加。為了解決這個問題,異步任務應運而生。通過將耗時操作移至后臺執行,異步任務可以避免阻塞主線程,提高應用程序的并發能力和響應速度。QJk28資訊網——每日最新資訊28at.com

異步任務的好處:QJk28資訊網——每日最新資訊28at.com

提高性能:異步任務可以避免阻塞主線程,使得應用程序能夠同時處理多個請求,提高了系統的吞吐量和性能。QJk28資訊網——每日最新資訊28at.com

改善用戶體驗:由于異步任務無需等待耗時操作完成,因此可以快速返回結果給用戶。這對于改善用戶體驗非常有益,用戶可以在短暫的等待時間后獲得響應,而無需長時間等待。QJk28資訊網——每日最新資訊28at.com

高效利用資源:異步任務可以充分利用系統資源,例如CPU和內存。在多核CPU系統中,異步任務可以同時運行多個任務,提高了資源的利用率。QJk28資訊網——每日最新資訊28at.com

降低系統負載:通過將耗時操作移至后臺執行,異步任務可以減輕前臺系統的負載,使其專注于處理核心業務邏輯。QJk28資訊網——每日最新資訊28at.com

適應高并發場景:在面對大量并發請求時,異步任務能夠更好地應對負載壓力,保證系統的穩定性和可用性。QJk28資訊網——每日最新資訊28at.com

總之,Spring異步任務為我們提供了一種高效處理耗時操作的方法,通過提高性能、改善用戶體驗、高效利用資源、降低系統負載以及適應高并發場景等方面的優勢,幫助我們構建更加出色的應用程序。QJk28資訊網——每日最新資訊28at.com

2. 實戰代碼

為了演示的方便,所有示例代碼我都將在一個類中完成。QJk28資訊網——每日最新資訊28at.com

在項目中要使用異步任務非常的簡單,我們只需要通過一個注解開啟即可,剩下的就只需要在需要異步執行的方法上添加上@Async注解即可。示例代碼如下:QJk28資訊網——每日最新資訊28at.com

通過@EnableAsync開啟異步任務QJk28資訊網——每日最新資訊28at.com

// 該配置類就作用就是開啟異步任務的能力@Configuration@EnableAsyncstatic class AppConfig {}

測試使用的組件類QJk28資訊網——每日最新資訊28at.com

@Componentstatic class AsyncService {    // 我們只需要在我們的方法上添加@Async即可  // 這樣該方法的執行將會在另外的線程中執行  @Async  public void calc() {    System.out.printf("執行線程: %s - 開始執行%n", Thread.currentThread().getName()) ;    try {      // 模擬耗時的操作      TimeUnit.SECONDS.sleep(2) ;    } catch (InterruptedException e) {      e.printStackTrace();    }    System.out.printf("線程: %s - 執行完成%n", Thread.currentThread().getName()) ;  }}

測試代碼QJk28資訊網——每日最新資訊28at.com

try (GenericApplicationContext context = new GenericApplicationContext()) {  // 容器中注冊相關的Bean  context.registerBean(ConfigurationClassPostProcessor.class) ;  context.registerBean(AppConfig.class) ;  context.registerBean(AsyncService.class) ;  context.refresh() ;  // 從容器中獲取組件  AsyncService as = context.getBean(AsyncService.class) ;  // 下面調用3次任務  as.calc() ;   as.calc() ;  as.calc() ;  System.out.println("主線程結束...") ;  System.in.read() ;}

執行結果QJk28資訊網——每日最新資訊28at.com

主線程結束...執行線程: SimpleAsyncTaskExecutor-1 - 開始執行執行線程: SimpleAsyncTaskExecutor-2 - 開始執行執行線程: SimpleAsyncTaskExecutor-3 - 開始執行線程: SimpleAsyncTaskExecutor-2 - 執行完成線程: SimpleAsyncTaskExecutor-1 - 執行完成線程: SimpleAsyncTaskExecutor-3 - 執行完成

主線程早早的執行完了,每次方法的調用都在不同的線程,與阻塞執行相比大大提高了系統的吞吐量。QJk28資訊網——每日最新資訊28at.com

使用就是這么簡單,但是我們還需要更加的深入了解這里異步執行的線程是什么樣的一個線程池?是否可以自定義自己的線程池?接下來就從這2個問題來更加的深入學習異步任務執行的原理。QJk28資訊網——每日最新資訊28at.com

3. 異步任務使用的線程池

在Spring中使用異步任務的底層原理主要是通過Spring AOP(面向切面編程)來實現的。AOP是一種編程思想,它通過在程序執行的關鍵點上添加橫切關注點,來提高代碼的復用性和可維護性。QJk28資訊網——每日最新資訊28at.com

在Spring異步任務中,AOP被用于攔截方法的執行,將耗時的任務放入線程池中執行,從而避免阻塞主線程。具體來說,Spring異步任務底層使用了Java的Future和Callable接口,以及線程池技術來實現異步執行。QJk28資訊網——每日最新資訊28at.com

首先,當我們在Spring中定義一個異步方法時,實際上該方法并不會立即執行,而是會被封裝為一個Callable對象。Callable接口與Runnable接口類似,但它可以返回結果,并可以拋出異常。QJk28資訊網——每日最新資訊28at.com

異步任務執行的核心處理器類是:AsyncAnnotationBeanPostProcessorQJk28資訊網——每日最新資訊28at.com

該處理器的創建是在@EnableAsync注解中的@Import導入的類QJk28資訊網——每日最新資訊28at.com

public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {  @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)  public AsyncAnnotationBeanPostProcessor asyncAdvisor() {    AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();    // 線程池是引用的父類中的成員    bpp.configure(this.executor, this.exceptionHandler);    return bpp;  }}// 父類AbstractAsyncConfiguration public abstract class AbstractAsyncConfiguration implements ImportAware {  protected Supplier<Executor> executor;  // 這里的入參是我們可以自定義實現的地方,后面會講到  @Autowired  void setConfigurers(ObjectProvider<AsyncConfigurer> configurers) {    Supplier<AsyncConfigurer> configurer = SingletonSupplier.of(() -> {      List<AsyncConfigurer> candidates = configurers.stream().collect(Collectors.toList());      if (CollectionUtils.isEmpty(candidates)) {        return null;      }      // 如果系統中定義了多個AsyncConfigurer將會拋出異常      if (candidates.size() > 1) {        throw new IllegalStateException("Only one AsyncConfigurer may exist");      }      return candidates.get(0);    });    // 如果沒有自定義,則調用AsyncConfigurer#getAsyncExecutor,默認這個方法返回的是null    // 所以,在默認情況下,這里的executor還是為null    this.executor = adapt(configurer, AsyncConfigurer::getAsyncExecutor);    this.exceptionHandler = adapt(configurer, AsyncConfigurer::getAsyncUncaughtExceptionHandler);  }}

接著進入核心的處理器類AsyncAnnotationBeanPostProcessor 該類中現在設置的executor還是為null。QJk28資訊網——每日最新資訊28at.com

public class AsyncAnnotationBeanPostProcessor {  // 在示例化當前處理器過程中會執行setBeanFactory方法  // 該方法中會定義AOP的切面(低級切面)Advisor  public void setBeanFactory(BeanFactory beanFactory) {    super.setBeanFactory(beanFactory);    // 該構造方法中會構建相應的通知及切入點    AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);  }}// 切面public class AsyncAnnotationAdvisor {  public AsyncAnnotationAdvisor(...) {    // 構建通知攔截器    this.advice = buildAdvice(executor, exceptionHandler);    this.pointcut = buildPointcut(asyncAnnotationTypes);  }  protected Advice buildAdvice() {    // 該攔截器說下繼承關系    // 1. AnnotationAsyncExecutionInterceptor繼承 AsyncExecutionInterceptor    // 2. AsyncExecutionInterceptor 繼承 AsyncExecutionAspectSupport    AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);    // 在該方法中進行初始化線程池    // 調用父類AsyncExecutionAspectSupport#configure方法    interceptor.configure(executor, exceptionHandler);    return interceptor;   }}public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport {  protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {    // 先調用父類,默認情況下父類返回null,下面有分析    Executor defaultExecutor = super.getDefaultExecutor(beanFactory);    // 當為null,這里就創建默認的線程池SimpleAsyncTaskExecutor    // 這也就是上面的示例代碼中默認線程池名稱打印的是SimpleAsyncTaskExecutor-*    return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());  }}public abstract class AsyncExecutionAspectSupport {  public void configure(@Nullable Supplier<Executor> defaultExecutor,      @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {    // defaultExecutor為null,則會獲取系統默認的getDefaultExecutor    // getDefaultExecutor這里的方法被子類AsyncExecutionInterceptor重寫了    this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory));  }  // 初始化系統默認的線程池  protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {    if (beanFactory != null) {      try {        // 從容器中查找TaskExcutor類型的Bean        return beanFactory.getBean(TaskExecutor.class);      } catch (NoUniqueBeanDefinitionException ex) {        try {          // 如果容器中有多個這種Bean,則在通過beanName獲取          // beanName = taskExecutor          return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);        }      } catch (NoSuchBeanDefinitionException ex) {        try {          // 如果指定beanName=taskExecutor類型為TaskExecutor的Bean          // 則在獲取beanName=taskExecutor類型為Executor類型的Bean          return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);        }      }    }    return null;  }}

分析到這,在我們當前的環境下是沒有TaskExecutor或Executor類型的Bean。所以程序這里最終返回還是null。那這個默認線程池是誰呢?繼續向下看QJk28資訊網——每日最新資訊28at.com

在上面的buildAdvice方法中構建攔截器AnnotationAsyncExecutionInterceptor該攔截器是執行的核心QJk28資訊網——每日最新資訊28at.com

public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor {  public Object invoke(final MethodInvocation invocation) throws Throwable {    // 確定任務執行的線程池    AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);  }}

到此分析完了Spring的異步任務執行使用線程池的情況。現總結下查找線程池的流程步驟:QJk28資訊網——每日最新資訊28at.com

  • 容器中查找AsyncConfigurer
  • 在1中沒有,則容器中查找TaskExecutor類型的Bean,如果正好有一個則使用,如果有多個則從容器中查找beanName=taskExecutor,類型為Executor,如果沒有則返回null。
  • 在2中如果沒有TaskExecutor類型的Bean,則從容器中查找beanName=taskExecutor,類型為Executor,如果沒有則返回null。
  • 到此都還是沒有,則直接創建SimpleAsyncTaskExecutor對象作為線程池。

QJk28資訊網——每日最新資訊28at.com

4. 自定義線程池

通過上面的分析你應該知道了如何自定義線程池了。QJk28資訊網——每日最新資訊28at.com

自定義AsyncConfigurerQJk28資訊網——每日最新資訊28at.com

@Componentstatic class CustomAsyncConfigurer implements AsyncConfigurer {  @Override  public Executor getAsyncExecutor() {    return new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadFactory() {      private final AtomicInteger poolNumber = new AtomicInteger(1);      private final ThreadGroup group = Thread.currentThread().getThreadGroup() ;      private final AtomicInteger threadNumber = new AtomicInteger(1);      private final String namePrefix = "pack-" + poolNumber.getAndIncrement() +"-thread-" ;      public Thread newThread(Runnable r) {          Thread t = new Thread(group, r,                                namePrefix + threadNumber.getAndIncrement(),                                0);          if (t.isDaemon())              t.setDaemon(false);          if (t.getPriority() != Thread.NORM_PRIORITY)              t.setPriority(Thread.NORM_PRIORITY);          return t;      }    }) ;  }}

在容器中注冊上面的bean后,執行結果如下:QJk28資訊網——每日最新資訊28at.com

主線程結束...執行線程: pack-1-thread-1 - 開始執行執行線程: pack-1-thread-2 - 開始執行線程: pack-1-thread-2 - 執行完成線程: pack-1-thread-1 - 執行完成執行線程: pack-1-thread-2 - 開始執行線程: pack-1-thread-2 - 執行完成

自定義線程池生效了。QJk28資訊網——每日最新資訊28at.com

其它方式就不嘗試了。QJk28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-15588-0.html提高系統性能的必備技能:異步任務完全指南

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 微服務Saas如何做私有化部署

下一篇: 常用的調度算法有哪些?你知道了嗎?

標簽:
  • 熱門焦點
  • Redmi Buds 4開箱簡評:才199還有降噪 可以無腦入

    在上個月舉辦的Redmi Note11T Pro系列新機發布會上,除了兩款手機新品之外,Redmi還帶來了兩款TWS真無線藍牙耳機產品,Redmi Buds 4和Redmi Buds 4 Pro,此前我們在Redmi Note11T
  • 之家push系統迭代之路

    前言在這個信息爆炸的互聯網時代,能夠及時準確獲取信息是當今社會要解決的關鍵問題之一。隨著之家用戶體量和內容規模的不斷增大,傳統的靠"主動拉"獲取信息的方式已不能滿足用
  • 如何使用JavaScript創建一只圖像放大鏡?

    譯者 | 布加迪審校 | 重樓如果您曾經瀏覽過購物網站,可能遇到過圖像放大功能。它可以讓您放大圖像的特定區域,以便瀏覽。結合這個小小的重要功能可以大大改善您網站的用戶體驗
  • 只需五步,使用start.spring.io快速入門Spring編程

    步驟1打開https://start.spring.io/,按照屏幕截圖中的內容創建項目,添加 Spring Web 依賴項,并單擊“生成”按鈕下載 .zip 文件,為下一步做準備。請在進入步驟2之前進行解壓。圖
  • 自動化在DevOps中的力量:簡化軟件開發和交付

    自動化在DevOps中扮演著重要角色,它提升了DevOps的效能。通過自動化工具和方法,DevOps團隊可以實現以下目標:消除手動和重復性任務。簡化流程。在整個軟件開發生命周期中實現更
  • Python異步IO編程的進程/線程通信實現

    這篇文章再講3種方式,同時講4中進程間通信的方式一、 Python 中線程間通信的實現方式共享變量共享變量是多個線程可以共同訪問的變量。在Python中,可以使用threading模塊中的L
  • 猿輔導與新東方的兩種“歸途”

    作者|卓心月 出品|零態LT(ID:LingTai_LT)如何成為一家偉大企業?答案一定是對&ldquo;勢&rdquo;的把握,這其中最關鍵的當屬對企業戰略的制定,且能夠站在未來看現在,即使這其中的
  • 信通院:小米、華為等11家應用商店基本完成APP簽名及驗簽工作

    中國信通院表示,目前,小米、華為、OPPO、vivo、360手機助手、百度手機助手、應用寶、豌豆莢和努比亞等9家應用商店,以及抖音和快手2家新型應用分發平
  • 三星折疊屏手機去年銷售近1000萬臺 今年目標定為1500萬

    7月29日消息,三星率先發力可折疊手機市場,在全球市場已經取得了非常亮眼的成績,接下來會進一步鞏固和擴大這一優勢。三星在推出Galaxy Z Flip5和Galax
Top 主站蜘蛛池模板: 深泽县| 建湖县| 东台市| 泰和县| 高唐县| 贵德县| 江川县| 仁怀市| 永兴县| 黄骅市| 封丘县| 贵阳市| 响水县| 泸定县| 刚察县| 南昌县| 十堰市| 太康县| 保靖县| 喜德县| 安顺市| 彭山县| 三河市| 和硕县| 醴陵市| 方城县| 罗山县| 五台县| 海原县| 白河县| 凤庆县| 绥宁县| 正镶白旗| 青龙| 湘潭市| 河北省| 鲁山县| 宜兰县| 嘉定区| 瑞丽市| 关岭|