@Async不是写上就完事儿,搭配线程池使用更香

355 阅读3分钟

线程池是为了对创建的线程进行复用,每次都创建,销毁线程浪费资源和时间。将一些没有强相关的任务丢到里边可以并行运行节省时间。

如何创建线程池?

Executors 方法中有一些默认的创建的线程池

ExecutorService executorService = Executors.newCachedThreadPool();
//该线程池会不断的创建非核心线程,当请求量很大时会造成OOM
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
                                  }


ExecutorService executorService = Executors.newFixedThreadPool();
//创建固定的数目的核心线程,请求很大会将请求阻塞在阻塞队列中,积压大量请求会导致OOM
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}


ExecutorService executorService = Executors.newSingleThreadExecutor();
//创建一个核心线程
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

ExecutorService executorService = Executors.newScheduledThreadPool();
//核心线程固定
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

JDK默认提供的默认线程池存在OOM的风险,因此使用的话结合自己的需求创建。

ThreadPoolExecutor构造方法


public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

corePoolSize:核心线程
maximumPoolSize:最大线程数
keepAliveTime:非核心线程keepAlive时间
unit:存活时间单位
workQueue:阻塞队列
handler:抛弃策略

1.任务来时先创建核心线程
2.当任务数量等于corePoolSize时,再来任务放到阻塞队列中,空闲的核心线程会从阻塞队列中拿任务处理
3.corePoolSize!=maximumPoolSize情况下,阻塞队列满了会创建非核心线程处理。非核心线程也会从阻塞队列中拿任务处理
4.1线程数量超到maximumPoolSize再来任务会直接拒绝策略
4.2线程数量为超过到maximumPoolSize,非核心线程达到keepAlive时间未拿到任务被销毁,核心线程拿不到任务被销毁

常用的阻塞队列:ArrayBlockingQueue 底层为数组的有界阻塞队列
LinkedBlockingQueue 底层为链表的有界阻塞队列
DelayedWorkQueue 任务到达一定的delay时间才会直接
SynchronousQueue 不存储元素的阻塞队列
PriorityBlockingQueue 按照优先级排序的无界队列

拒绝策略:
ThreadPoolExecutor.AbortPolicy 默认拒绝策略 抛异常
ThreadPoolExecutor.DiscardPolicy 抛弃任务
ThreadPoolExecutor.DiscardOldestPolicy 抛弃最旧的任务
ThreadPoolExecutor.CallerRunsPolicy 在调用execute方法的线程中运行被拒绝的任务

@Async 执行异步任务 将不重要或者非常耗时的操作放到线程池中进行异步操作,避免对主流程进行长时间的阻塞

1.在主方法上加注解@EnableAsync

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

2.在需要异步的方法上加 @Async注解

3.使用线程池

如果不配置线程池的话会使用以下默认配置的线程池(ThreadPoolTaskExecutor)
private int corePoolSize = 1;
private int maxPoolSize = Integer.MAX_VALUE;
private int keepAliveSeconds = 60;
private int queueCapacity = Integer.MAX_VALUE;
private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();

protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
    return (BlockingQueue)(queueCapacity > 0 ? new LinkedBlockingQueue(queueCapacity) : new SynchronousQueue());
}

配置线程池

@Configuration 
public class AsyncConfig { 
    @Bean("executor1")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() { 
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
        executor.setCorePoolSize(10); //配置核心线程数
        executor.setMaxPoolSize(30); //配置最大线程数 
        executor.setQueueCapacity(200); //配置队列大小 
        executor.setKeepAliveSeconds(30); //线程池维护线程所允许的空闲时间
        executor.setThreadNamePrefix("test_"); //配置线程池中的线程的名称前缀
        executor.setWaitForTasksToCompleteOnShutdown(true); //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
        executor.setAwaitTerminationSeconds(60); //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 设置拒绝策略
        executor.initialize(); //执行初始化 
        return executor; 
    } 
}
ThreadPoolTaskExecutor 是对 ThreadPoolExecutor的一个封装
创建多个线程池调用可以根据name区分。例如@Async("executor1")