3

使用前提

  1. 假如当前类 a.class 中有异步方法,并使用了@Async,那么必须由其他类(例如b.class)来调用,不可由其本身(a.class)来调用;
  2. 该方法必须是public 且无返回值,即:

        @Async
        public void asyncMethod(){
            
        }

需要注意

  1. 在和事务一起使用的时候,即调用这个异步方法的方法上使用了@Transactional注解。
    a.class

        @Async
        public void asyncMethod(){
            
        }

    b.class

        @Transactional
        public void fun(){
            //...
            a.asyncMethod();
            //...
        }

    此时事务会有问题,因为a会新起一个线程来执行asyncMethod(),所以a与fun()不是一个事务,会导致数据不一致。

2.需要为异步配置线程池
如下

package com.fanneng.spring.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created by feifan.gou@gmail.com on 2019/11/29 20:00.
 */
@Configuration
@EnableAsync
public class ThreadPoolConfig {

    @Autowired
    private ThreadPoolProperty poolProperty;

    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        /* 最大线程数 */
        executor.setMaxPoolSize(poolProperty.maxPoolSize);
        /* 核心线程数 */
        executor.setCorePoolSize(poolProperty.corePoolSize);
        /* 队列最大长度 */
        executor.setQueueCapacity(poolProperty.queueCapacity);
        /* 线程名称前缀 */
        executor.setThreadNamePrefix(poolProperty.threadNamePrefix);
        /* 拒绝策略 */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Getter
    @Setter
    @Configuration
    @ConfigurationProperties(prefix = "executor.thread.pool")
    public static class ThreadPoolProperty {

        private int corePoolSize;
        private int maxPoolSize;
        private int queueCapacity;
        private String threadNamePrefix;
    }
    /* 拒绝策略说明(当pool已经达到max size的时候,如何处理新任务):
     * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
     * ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
     * ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
     * ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
     */
}
在注入ThreadPoolTaskExecutor的时候需要注意

public ThreadPoolTaskExecutor taskExecutor()中的方法名默认是taskExecutor,那么意味着bean的名称也是taskExecutor,那么在使用@Async的时候,会默认去该线程池那线程;
为啥默认是taskExecutor呢? 请看spring源码 TaskExecutionAutoConfiguration.java 84行:微信截图_20191205105740.png

如果起了其他的名称,也就是bean是其他名称,那么在使用@Async的时候需要指定线程池的名称;例如bean的名称是 asyncExector ,那么使用@Async的时候应该是

       @Async("asyncExector")
       public void asyncMethod(){
           
       }

否则会去默认使用 SimpleAsyncTaskExecutor。

附上spring 中 TaskExecutor 的常用几个实现

名字 特点
SimpleAsyncTaskExecutor 每次请求新开线程,没有最大线程数设置.不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。 --【1】
SyncTaskExecutor 不是异步的线程.同步可以用SyncTaskExecutor,但这个可以说不算一个线程池,因为还在原线程执行。这个类没有实现异步调用,只是一个同步操作。
ConcurrentTaskExecutor Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
SimpleThreadPoolTaskExecutor 监听Spring’s lifecycle callbacks,并且可以和Quartz的Component兼容.是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。
ThreadPoolTaskExecutor 最常用。要求jdk版本大于等于5。可以在程序而不是xml里修改线程池的配置.其实质是对java.util.concurrent.ThreadPoolExecutor的包装。

Crayon2f_
24 声望1 粉丝

每天进步一点点


« 上一篇
java 8 中的坑
下一篇 »
logback.xml 配置