Async注解有什么缺点

缺点

  • @Async中关于线程池的使用部分在AsyncExecutionInterceptor中,在这个类中有一个getDefaultExecutor方法, 当我们没有做过自定义线程池的时候,就会用SimpleAsyncTaskExecutor这个线程池。

  • @Override
    protected Executor getDefaultExecutor(BeanFactory beanFactory) {
        Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
        return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    - SimpleAsyncTaskExecutor这玩意坑很大,其实他并不是真的线程池,它是不会重用线程的,每次调用都会创建一个新的线程,也没有最大线程数设置。并发大的时候会产生严重的性能问题。

    - 在没有Spring boot的加持下,异步任务使用了一个线程池,它的corePoolSize=8, 阻塞队列采用了无界队列LinkedBlockingQueue。一旦采用了这样的组合,最大线程数就会形同虚设,因为超出8个线程的任务,将全部会被放到无界队列里。

    - 因此我们对于@Async注解的使用是极度容易造成OOM的,更何况我们对于自定义线程池都不敢说完全了解,就不要使用该注解啦。

    ## 如何解决

    ```java
    @Configuration
    @EnableAsync
    public class AsyncExecutorConfig {
    @Bean("registerSuccessExecutor")
    public Executor caseStartFinishExecutor() {

    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    .setNameFormat("registerSuccessExecutor-%d").build();

    ExecutorService executorService = new ThreadPoolExecutor(100, 200,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    return executorService;
    }

    }
  • 我们通过@Async(“registerSuccessExecutor”)便可以将异步线程池指定为我们自定义的线程池,也就一定程度上的弥补该注解的问题。

扩展知识

时间监听机制(观察者模式)

Spring Event是Spring框架中的一种事件机制,它允许不同组件之间通过事件的方式进行通信。Spring框架中的事件机制建立在观察者模式的基础上,允许应用程序中的组件注册监听器来监听特定类型的事件,并在事件发生时执行相应的操作。

Spring Event的使用需要定义以下三个内容:

  1. 事件(Event):事件是一个普通的Java对象,用于封装关于事件发生的信息。通常,事件类会包含一些数据字段,以便监听器能够获取事件的相关信息。
  2. 事件发布者(Event Publisher):事件发布者是负责触发事件并通知所有注册的监听器的组件。
  3. 事件监听器(Event Listener):事件监听器是负责响应特定类型事件的组件。它们实现了一个接口或者使用注解来标识自己是一个事件监听器,并定义了在事件发生时需要执行的逻辑。

如何实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class RegisterSuccessEvent extends ApplicationEvent {

public RegisterSuccessEvent(RegisterInfo registerInfo) {
super(registerInfo);
}
}
// --------------------------------------------------------------
@Service
public class RegisterService{
@Autowired
protected ApplicationContext applicationContext;

public RegisterResponse register(RegisterInfo registerInfo){

//用户注册核心代码

//发送一个注册完成的事件
applicationContext.publishEvent(new RegisterSuccessEvent(registerInfo));

}
}
// --------------------------------------------------------------
@Component
public class RegisterEventListener {

@EventListener(RegisterSuccessEvent.class)
public void onApplicationEvent(RegisterSuccessEvent event) {
RegisterInfo registerInfo = (RegisterInfo) event.getSource();
//注册完成逻辑
}
}

默认情况下,Spring Event的调用时同步调用的。如果想要实现异步调用,也是支持的,最简单的方式就是借助@Async 注解对监听器进行标注,当然实际情况必然是不建议的,我们应当使用自定义线程池进行异步通知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	@Override
public <T> void publish(String channel, T event) {
List<EventSubscriber<T>> eventSubscribers =
(List) this.essProviders.entrySet()
.stream()
.filter((entry) -> {
return ((SubscriptionKey) entry.getKey()).matches(channel, event);
})
.flatMap((entry) -> {
return ((EssProvider) entry.getValue()).get().getEventSubscribers().stream();
})
.map((eventSubscriber) -> {
return eventSubscriber;
})
.collect(Collectors.toList());
this.delivery.publishTo(eventSubscribers, event);
}
// --------------------------------------------------------------
// 我们可以通过Bean初始化机制对我们自定义的通知注解进行加载进CurrentHashMap中,当我们进行实践通知时便可以对不同的监听器进行通知

好处

  1. 代码解耦:通过使用事件机制,组件之间不需要直接互相依赖,从而减少了代码之间的耦合度。这使得代码更易于维护和扩展。
  2. 职责清晰:事件机制有助于将应用程序拆分为更小的模块,每个模块只关心要做的事儿,和关心自己需要监听的事件就行了。
  3. 异步处理:Spring Event机制支持异步事件处理,这意味着可以将事件的处理分发到不同的线程,提高了系统的响应性。
  4. 一对多:Spring Event是观察者模式的一种实现,他的一个事件可以有多个监听者,所以当我们有多个模块之间需要通信时,可以直接发一个事件出去,让监听者各自监听即可。

Async注解有什么缺点
http://lzhnet.top/2023/06/12/Async注解有什么缺点/
Author
kuaile000
Posted on
June 12, 2023
Licensed under