Spring @Async踩到的坑

 

背景

Spring中用@Async注解标记的方法,称为异步方法,其实就相当于我们自己在当前方法里面:new Thread(()-> System.out.println("hello world !"))。

 按@Async注解使用的基本方法:

  1. 在方法上添加@Async注解;
  2. 所使用的@Async注解方法的类对象应该是Spring容器管理的bean对象;
  3. 调用异步方法类上需要配置上注解@EnableAsync

结果发现理想很丰满,现实很骨感,并没有实现异步。原因有以下:

不要在同一个类里面调用异步方法

源码

public interface MyTestService {
  void sleep(int n);
  void test();
}

@Service
public class MyTestServiceImpl implements MyTestService {

  @Autowired
  private AsyncMyTestService asyncMyTestService;

  public void sleep(int n) {
    try {
      Thread.sleep(n * 1000);
    } catch (Exception e) {

    }
  }

  public void test() {
    System.out.println("test");
    asyncMethod();//同一个类里面调用异步方法
  }

  @Async
  public void asyncMethod() {
    sleep(10);
    System.out.println("async");
  }
}
 下面开始执行异步,发现异步没有生效。
	@Autowired
	private MyTestService myTestService;
	@Override
	public void run(String... args) throws Exception {
		long begin = System.currentTimeMillis();
		myTestService.test();
		System.out.println((System.currentTimeMillis() - begin) / 1000);
	}

原因是如果调用方和被调用方是在同一个类中,是无法产生切面的,@Async没有被Spring容器管理。

然后有些朋友就开始通过@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) 再通过AopContext.currentProxy()拿到当前Spring容器管理的Service的代理类 

((MyTestServiceImpl) AopContext.currentProxy()).asyncMethod();

再搜索一番解决如下报错,终于实现了异步。

java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.

杜绝AopContext.currentProxy()

AopContext.currentProxy()破坏了面向接口的编程,也包含了很多潜在的坑,应该禁用。要用异步就好好新建一个单独的异步类。

 public interface AsyncMyTestService {
  void asyncMethod();
}


@Autowired
  private AsyncMyTestService asyncMyTestService;
  public void test() {
    System.out.println("test");
    asyncMyTestService.asyncMethod();
  }

没有定义线程池

如果不自定义异步方法的线程池默认使用SimpleAsyncTaskExecutor。SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。并发大的时候会产生严重的性能问题。

定义通用线程池

@EnableAsync
@Configuration
public class ThreadTestConfig {
  @Bean
  public Executor taskExecutor() {
    ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
    pool.setCorePoolSize(5); //线程池活跃的线程数
    pool.setMaxPoolSize(10); //线程池最大活跃的线程数
    pool.setWaitForTasksToCompleteOnShutdown(true);
    pool.setThreadNamePrefix("lalala");
    return pool;
  }
}

@Async默认使用Bean Name为executor的线程池。也可以根据Bean Name指定特定线程池

@Async("taskExecuters2")
  public void asyncMethod() {
    sleep(10);
    System.out.println("async");
  }

 

你可能感兴趣的:(java)