最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

@Async注解的使用以及注解失效問(wèn)題的解決

 更新時(shí)間:2024年09月19日 09:45:04   作者:豆腐腦lr  
在Spring框架中,@Async注解用于聲明異步任務(wù),可以修飾類(lèi)或方法,使用@Async時(shí),必須確保方法為public,且類(lèi)為Spring管理的Bean,啟用異步任務(wù)需要在主類(lèi)上添加@EnableAsync注解,默認(rèn)線程池為SimpleAsyncTaskExecutor

1. @Async作用范圍

@Async的注解如下,可以看出該注解可以修飾類(lèi)方法。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
    String value() default "";
}

該注解使用要滿(mǎn)足以下基本要求:

  • 1)在方法上使用該@Async注解,申明該方法是一個(gè)異步任務(wù);(必須是public的方法,不能是private的方法,否則注解會(huì)失效?。。?/strong>
  • 2)在類(lèi)上面使用該@Async注解,申明該類(lèi)中的所有方法都是異步任務(wù);
  • 3)方法上一旦標(biāo)記了這個(gè)@Async注解,當(dāng)其它線程調(diào)用這個(gè)方法時(shí),就會(huì)開(kāi)啟一個(gè)新的子線程去異步處理該業(yè)務(wù)邏輯。
  • 4)使用此注解的方法的類(lèi)對(duì)象,必須是spring管理下的bean對(duì)象 (如被@Service、@Component等修飾的Bean對(duì)象)
  • 5)要想使用異步任務(wù),需要在主類(lèi)上開(kāi)啟異步配置,即配置上@EnableAsync注解

2. 基本使用方法

2.1 開(kāi)啟異步注解@EnableAsync

在SpringBoot的啟動(dòng)類(lèi)上開(kāi)啟異步任務(wù)注解

@SpringBootApplication
@EnableAsync
public class AsyncDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(AsyncDemoApplication.class, args);
    }
}

2.2 創(chuàng)建Bean對(duì)象及異步方法

@Component
public class Aservice {
    @Async
    public void MethodA() {
        System.out.println("當(dāng)前線程為:" + Thread.currentThread().getName());
    }
}

2.3 在Test方法中進(jìn)行測(cè)試

@SpringBootTest
class AsyncDemoApplicationTests {

    @Autowired
    private Aservice aservice;
    @Test
    void contextLoads() {
        System.out.println("當(dāng)前線程名稱(chēng):" + Thread.currentThread().getName());
        aservice.MethodA();
    }
}

測(cè)試結(jié)果如下,可以看到確實(shí)開(kāi)啟了一個(gè)異步任務(wù)。

  • 當(dāng)前線程名稱(chēng):main
  • 當(dāng)前線程為:task-1

2.4 隱藏問(wèn)題:默認(rèn)線程池配置不合適,導(dǎo)致系統(tǒng)奔潰

@Async注解在使用時(shí),如果不指定線程池的名稱(chēng),則使用Spring默認(rèn)的線程池,Spring默認(rèn)的線程池為SimpleAsyncTaskExecutor。

該類(lèi)型線程池的默認(rèn)配置:

  • 默認(rèn)核心線程數(shù):8,
  • 最大線程數(shù):Integet.MAX_VALUE,
  • 隊(duì)列使用LinkedBlockingQueue,
  • 容量是:Integet.MAX_VALUE,
  • 空閑線程保留時(shí)間:60s,
  • 線程池拒絕策略:AbortPolicy。

解決方法1: 修改配置文件,指定線程池參數(shù)

通過(guò)修改SpringBoot的配置文件application.yml來(lái)解決上述問(wèn)題:

spring:
  task:
    execution:
      thread-name-prefix: MyTask
      pool:
        max-size: 6
        core-size: 3
        keep-alive: 30s
        queue-capacity: 500

解決方法2:編寫(xiě)配置類(lèi)

首先在application.yml文件中自定義一些鍵值對(duì)。

mytask:
  execution:
    thread-name-prefix: myThread
    pool:
      max-size: 6
      core-size: 3
      keep-alive: 30
      queue-capacity: 500

然后編寫(xiě)一個(gè)集成了AsyncConfig的配置類(lèi)

// 如果沒(méi)有在啟動(dòng)類(lèi)上加注解,在異步任務(wù)配置類(lèi)中加也是可以的
@EnableAsync
@Configuration
public class AsyncExecutorConfig implements AsyncConfigurer {

    @Value(value="${mytask.execution.pool.core-size}")
    private String CORE_SIZE;

    @Value(value="${mytask.execution.pool.max-size}")
    private String MAX_SIZE;

    @Value("${mytask.execution.pool.queue-capacity}")
    private String QUEUE_SIZE;

    @Value("${mytask.execution.thread-name-prefix}")
    private String THREAD_NAME_PREFIX;

    @Value("${mytask.execution.pool.keep-alive}")
    private int KEEP_ALIVE;
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(Integer.parseInt(CORE_SIZE));
        executor.setMaxPoolSize(Integer.parseInt(MAX_SIZE));
        executor.setQueueCapacity(Integer.parseInt(QUEUE_SIZE));
        executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        executor.setKeepAliveSeconds(KEEP_ALIVE);
        executor.setRejectedExecutionHandler(
                (runnable, threadPoolExecutor) -> {
                    try {
                        threadPoolExecutor.getQueue().put(runnable);
                    } catch (InterruptedException e) {
                        System.out.println("Thread pool receives InterruptedException: " + e);
                    }
                });
        executor.initialize();
        return executor;
    }
}

這樣在啟動(dòng)上述任務(wù),就會(huì)打印出修改后的線程名稱(chēng)。

3. 帶返回值和不帶返回值的異步任務(wù)

3.1 不帶返回值的異步任務(wù)。

AService.java中新增異步方法:

    @Async
    public void MethodB() {
        for (int i = 0; i < 5; i++) {
            // 模擬任務(wù)執(zhí)行需要5秒
            System.out.println("線程-" + Thread.currentThread().getName() + "-業(yè)務(wù)" + i + "執(zhí)行中...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

為了方便測(cè)試,編寫(xiě)一個(gè)Controller接口,來(lái)測(cè)試該方法。

@RestController
public class TestController {
    @Autowired
    private Aservice aservice;
    @GetMapping("/test1")
    public String test1() {
        System.out.println(Thread.currentThread().getName() + "線程開(kāi)始...");
        long start = System.currentTimeMillis();
        aservice.MethodB();
        long end = System.currentTimeMillis();
        return "一共耗時(shí):" + (end -start) + "毫秒";
    }
}

在瀏覽器訪問(wèn)對(duì)應(yīng)接口,發(fā)現(xiàn)僅用了幾毫秒的時(shí)間,實(shí)際MethodB的執(zhí)行時(shí)間為5秒,說(shuō)明異步方法成功。

3.2 帶返回結(jié)果的異步任務(wù)。

編寫(xiě)一個(gè)帶返回結(jié)果的異步任務(wù)。

    @Async
    public Future<Integer> methodC() {
        // 模擬業(yè)務(wù) 執(zhí)行需要5秒
        System.out.println("當(dāng)前線程為:" + Thread.currentThread().getName());
        Integer result = null;
        for (int i = 0; i < 5; i++) {
            // 模擬任務(wù)執(zhí)行需要5秒
            System.out.println("線程-" + Thread.currentThread().getName() + "-業(yè)務(wù)" + i + "執(zhí)行中...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        result = 1; // 5秒后得到處理后的數(shù)據(jù)
        System.out.println("methodC 執(zhí)行完畢");
        return new AsyncResult<>(result);
    }

在控制層進(jìn)行調(diào)用,為了驗(yàn)證異步的效果,在控制層也加入3秒中的sleep().

    @GetMapping("/getResult")
    public Integer getResult() throws ExecutionException, InterruptedException {
        System.out.println(Thread.currentThread().getName() + "線程開(kāi)始...");
        long start = System.currentTimeMillis();
        Future<Integer> future = aservice.methodC();
        Thread.sleep(3000);
        Integer result = future.get();
        long end = System.currentTimeMillis();
        System.out.println("一共耗時(shí):" + (end - start) + "毫秒");
        return result;
    }

執(zhí)行結(jié)果如下,可以看出,盡管主線程中加入了3秒的休眠,整個(gè)任務(wù)還是只用了5秒的異步任務(wù)處理時(shí)長(zhǎng),說(shuō)明任務(wù)是在異步執(zhí)行的。

http-nio-8086-exec-1線程開(kāi)始...
當(dāng)前線程為:myThread1
線程-myThread1-業(yè)務(wù)0執(zhí)行中...
線程-myThread1-業(yè)務(wù)1執(zhí)行中...
線程-myThread1-業(yè)務(wù)2執(zhí)行中...
線程-myThread1-業(yè)務(wù)3執(zhí)行中...
線程-myThread1-業(yè)務(wù)4執(zhí)行中...
methodC 執(zhí)行完畢
一共耗時(shí):5053毫秒

有些教程上面可能會(huì)直接在開(kāi)啟異步任務(wù)的時(shí)候就進(jìn)行g(shù)et()了,這種方法雖然開(kāi)啟了額外的線程,但主方法其實(shí)也堵塞在get()這行代碼了,相當(dāng)于就還是同步方法了。

如下:

    @GetMapping("/getResult1")
    public Integer getResult1() throws ExecutionException, InterruptedException {
        System.out.println(Thread.currentThread().getName() #43; "線程開(kāi)始...");
        long start = System.currentTimeMillis();
        Integer result = aservice.methodC().get();
        Thread.sleep(3000);
        long end = System.currentTimeMillis();
        System.out.println("一共耗時(shí):" + (end - start) + "毫秒");
        return result;
    }

通過(guò)運(yùn)行結(jié)果可以看出,一共耗時(shí)8秒,如果是異步任務(wù),只需要5秒。

http-nio-8086-exec-1線程開(kāi)始...
當(dāng)前線程為:myThread1
線程-myThread1-業(yè)務(wù)0執(zhí)行中...
線程-myThread1-業(yè)務(wù)1執(zhí)行中...
線程-myThread1-業(yè)務(wù)2執(zhí)行中...
線程-myThread1-業(yè)務(wù)3執(zhí)行中...
線程-myThread1-業(yè)務(wù)4執(zhí)行中...
methodC 執(zhí)行完畢
一共耗時(shí):8049毫秒

4. 注解失效的可能原因及解決方法

4.1 異步方法修飾符非public

對(duì)于異步任務(wù),要使用public修飾符

@Component
public class Aservice {
    @Async
    public void MethodA() {
        System.out.println("當(dāng)前線程為:" + Thread.currentThread().getName());
    }
}

4.2 未開(kāi)啟異步配置

需要在SpringBoot啟動(dòng)類(lèi)上添加@EnableAsync注解

@SpringBootApplication
@EnableAsync//開(kāi)啟異步線程配置
public class AsyncDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(AsyncDemoApplication.class, args);
    }
}

或者在Aysnc配置類(lèi)上添加@EnableAsync注解

// 如果沒(méi)有在啟動(dòng)類(lèi)上加注解,在異步任務(wù)配置類(lèi)中加也是可以的
@EnableAsync
@Configuration
public class AsyncExecutorConfig implements AsyncConfigurer {

    @Value(value="${mytask.execution.pool.core-size}")
    private String CORE_SIZE;

    @Value(value="${mytask.execution.pool.max-size}")
    private String MAX_SIZE;

    @Value("${mytask.execution.pool.queue-capacity}")
    private String QUEUE_SIZE;

    @Value("${mytask.execution.thread-name-prefix}")
    private String THREAD_NAME_PREFIX;

    @Value("${mytask.execution.pool.keep-alive}")
    private int KEEP_ALIVE;
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(Integer.parseInt(CORE_SIZE));
        executor.setMaxPoolSize(Integer.parseInt(MAX_SIZE));
        executor.setQueueCapacity(Integer.parseInt(QUEUE_SIZE));
        executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        executor.setKeepAliveSeconds(KEEP_ALIVE);
        executor.setRejectedExecutionHandler(
                (runnable, threadPoolExecutor) -> {
                    try {
                        threadPoolExecutor.getQueue().put(runnable);
                    } catch (InterruptedException e) {
                        System.out.println("Thread pool receives InterruptedException: " + e);
                    }
                });
        executor.initialize();
        return executor;
    }
}

4.3 同一個(gè)類(lèi)的普通方法調(diào)用異步方法

如果在一個(gè)類(lèi)中,方法A被@Async修飾,而方法B沒(méi)有被@Async修飾,并且方法B調(diào)用了方法A,那么會(huì)導(dǎo)致@Async修飾的方法A的注解失效。原因是,對(duì)于對(duì)于加了@Async的方法A是通過(guò)SpringAOP機(jī)制生成的代理類(lèi)執(zhí)行的,方法B是直接調(diào)用這個(gè)類(lèi)的方法,因此通過(guò)B調(diào)用A,會(huì)使得A也被Spring當(dāng)成普通方法直接調(diào)用,從而使得注解失效。

可以通過(guò)以下兩種方式來(lái)確保@Async注解生效:

方法1: 將方法A的調(diào)用放在另外一個(gè)Bean上,并通過(guò)依賴(lài)注入的方式使用該Bean。

@Component
public class MyClass {
    private final MyAsyncService myAsyncService;

    public MyClass(MyAsyncService myAsyncService) {
        this.myAsyncService = myAsyncService;
    }

    @Async
    public void A() {
        // 異步操作內(nèi)容
    }

    public void B() {
        myAsyncService.A();
    }
}

@Service
public class MyAsyncService {
    @Async
    public void A() {
        // 異步操作內(nèi)容
    }
}

在上述示例中,MyClass類(lèi)中的方法B調(diào)用了MyAsyncService類(lèi)中的方法A。由于MyClass類(lèi)和MyAsyncService類(lèi)是不同的Bean,在MyClass中直接調(diào)用myAsnycService.A()時(shí),會(huì)觸發(fā)異步操作。

方法2. 在同一個(gè)類(lèi)內(nèi)部使用self-invocation的方式來(lái)調(diào)用被@Async修飾的方法。

@Service
public class MyService {
    @Autowired
    private MyService self;

   @Async
   public void A() {
       // 異步操作內(nèi)容
   }

   public void B() {
       self.A(); // 使用self-invocation調(diào)用被@Async修飾的方法A()
   }
}

在上述示例中,MyService類(lèi)內(nèi)部使用@Autowired將自身注入到了self變量中,在B()方法中通過(guò)self.A()來(lái)調(diào)用被@Async修飾的A()方法。這樣可以繞過(guò)Spring代理機(jī)制,保證A()方法能夠以異步方式執(zhí)行。

無(wú)論采取哪種方式,都能確保被@Asnyc修飾的方法在調(diào)用時(shí)能夠以異步方式執(zhí)行,而非直接在當(dāng)前線程執(zhí)行

上述代碼,其實(shí)存在一個(gè)問(wèn)題,即:因?yàn)?code>MyService類(lèi)中使用了自身的實(shí)例作為依賴(lài)。這種情況下,使用@Autowired注入會(huì)導(dǎo)致循環(huán)依賴(lài)。解決這個(gè)問(wèn)題有幾種方法:

  1. 使用@Lazy注解:將依賴(lài)的注入方式改為懶加載模式,即在需要使用時(shí)才進(jìn)行實(shí)例化。您可以將@Autowired注解改為@Autowired @Lazy,以解決循環(huán)依賴(lài)的問(wèn)題。
@Service
public class MyService {
    @Autowired
    @Lazy
    private MyService self;

   @Async
   public void A() {
       // 異步操作內(nèi)容
   }

   public void B() {
       self.A(); // 使用self-invocation調(diào)用被@Async修飾的方法A()
   }
}
  1. 使用構(gòu)造函數(shù)注入:將依賴(lài)通過(guò)構(gòu)造函數(shù)進(jìn)行注入而不是字段注入。這樣可以避免循環(huán)依賴(lài),因?yàn)樵跇?gòu)造對(duì)象時(shí)就能明確傳遞依賴(lài)關(guān)系。
@Service
public class MyService {
    private final MyService self;

    @Autowired
    public MyService(MyService self) {
        this.self = self;
    }

   @Async
   public void A() {
       // 異步操作內(nèi)容
   }

   public void B() {
       self.A(); // 使用self-invocation調(diào)用被@Async修飾的方法A()
   }
}

至于用哪種方法,可以根據(jù)實(shí)際需求選擇適合你場(chǎng)景的解決方案。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springboot+vue項(xiàng)目從第一行代碼到上線部署全流程

    springboot+vue項(xiàng)目從第一行代碼到上線部署全流程

    本文詳細(xì)介紹了如何從零開(kāi)始搭建一個(gè)基于Spring Boot和Vue.js的前后端分離項(xiàng)目,并涵蓋項(xiàng)目需求分析、技術(shù)選型、項(xiàng)目結(jié)構(gòu)設(shè)計(jì)、前后端交互、部署上線等全過(guò)程,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • SpringBoot下載docx文檔方式

    SpringBoot下載docx文檔方式

    這篇文章主要介紹了SpringBoot下載docx文檔方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • Java 程序里transient關(guān)鍵字使用方法示例

    Java 程序里transient關(guān)鍵字使用方法示例

    這篇文章主要為大家介紹了Java 程序里transient關(guān)鍵字使用方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Spring MVC學(xué)習(xí)之DispatcherServlet請(qǐng)求處理詳析

    Spring MVC學(xué)習(xí)之DispatcherServlet請(qǐng)求處理詳析

    這篇文章主要給大家介紹了關(guān)于Spring MVC學(xué)習(xí)教程之DispatcherServlet請(qǐng)求處理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • Java  Thread多線程詳解及用法解析

    Java Thread多線程詳解及用法解析

    本文主要介紹Java 多線程詳解及用法,這里整理了詳細(xì)資料及簡(jiǎn)單實(shí)現(xiàn)代碼,有需要的小伙伴可以參考下
    2016-09-09
  • Java自定義equals產(chǎn)生的問(wèn)題分析

    Java自定義equals產(chǎn)生的問(wèn)題分析

    這篇文章主要介紹了Java自定義equals時(shí)super.equals帶來(lái)的問(wèn)題分析,總的來(lái)說(shuō)這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達(dá)的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過(guò)程。希望通過(guò)這道題能給你帶來(lái)一種解題優(yōu)化的思路
    2023-01-01
  • 教你如何使用Java8實(shí)現(xiàn)菜單樹(shù)形數(shù)據(jù)

    教你如何使用Java8實(shí)現(xiàn)菜單樹(shù)形數(shù)據(jù)

    今天給大家?guī)?lái)的是關(guān)于JAVA的相關(guān)知識(shí),文中圍繞著如何使用Java8實(shí)現(xiàn)菜單樹(shù)形數(shù)據(jù)展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • SpringCloud集成Eureka并實(shí)現(xiàn)負(fù)載均衡的過(guò)程詳解

    SpringCloud集成Eureka并實(shí)現(xiàn)負(fù)載均衡的過(guò)程詳解

    這篇文章主要給大家詳細(xì)介紹了SpringCloud集成Eureka并實(shí)現(xiàn)負(fù)載均衡的過(guò)程,文章通過(guò)代碼示例和圖文講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考價(jià)值,需要的朋友可以參考下
    2023-11-11
  • Java HashMap在遍歷時(shí)刪除元素的實(shí)現(xiàn)

    Java HashMap在遍歷時(shí)刪除元素的實(shí)現(xiàn)

    本文主要介紹了Java HashMap在遍歷時(shí)刪除元素的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • Springboot @Value使用代碼實(shí)例

    Springboot @Value使用代碼實(shí)例

    這篇文章主要介紹了Springboot @Value使用代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論

大方县| 普兰县| 高邑县| 元氏县| 莱芜市| 彰化县| 湘阴县| 澄城县| 五原县| 赫章县| 满城县| 平阳县| 新民市| 灵丘县| 平乡县| 兴城市| 兴和县| 湾仔区| 琼海市| 鱼台县| 县级市| 昭通市| 阳原县| 昌邑市| 房产| 宣威市| 安宁市| 英山县| 井研县| 博湖县| 聂拉木县| 濮阳市| 太和县| 桦南县| 临夏市| 剑河县| 广丰县| 宝鸡市| 汕头市| 兴海县| 海门市|