@Async 注解的使用和实现

1.使用

1.1 SpringBootApplication 启动类或者配置类中添加@EnableAsync 注解。
1.2 在需要异步执行的方法中加上@Async 注解。

注意:

@Async 注解应该用在“public”方法上。
@Async 注解所在的类应该被 Spring 容器管理。
@Async 注解作用的方法,被调用的时候需要被Spring动态代理到,同类方法不能直接用this.xxx()调用否则不生效。

@EnableAsync
@SpringBootApplication
public class DemoAsyncApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoAsyncApplication.class, args);
    }

}
@Slf4j
@Service
public class AsyncService {

    @Async
    public void test(){
        try {
            TimeUnit.SECONDS.sleep(1);
            log.info(Thread.currentThread().getName()+"AsyncService");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }
    
}

2.实现

@Async 注解 的默认使用的是bean name为”taskExecutor“,核心线程数为8,最大线程数为Integer.MAX_VALUE,阻塞队列为LinkedBlockingQueue,阻塞队列的大小Integer.MAX_VALUE,为线程空闲超时时间为60秒。

public static class Pool {

		/**
		 * Queue capacity. An unbounded capacity does not increase the pool and therefore
		 * ignores the "max-size" property.
		 */
		private int queueCapacity = Integer.MAX_VALUE;

		/**
		 * Core number of threads.
		 */
		private int coreSize = 8;

		/**
		 * Maximum allowed number of threads. If tasks are filling up the queue, the pool
		 * can expand up to that size to accommodate the load. Ignored if the queue is
		 * unbounded.
		 */
		private int maxSize = Integer.MAX_VALUE;

		/**
		 * Whether core threads are allowed to time out. This enables dynamic growing and
		 * shrinking of the pool.
		 */
		private boolean allowCoreThreadTimeout = true;

		/**
		 * Time limit for which threads may remain idle before being terminated.
		 */
		private Duration keepAlive = Duration.ofSeconds(60);

使用的默认线程是在配置类TaskExecutionAutoConfiguration 里配置的。

/**
 * {@link EnableAutoConfiguration Auto-configuration} for {@link TaskExecutor}.
 *
 * @author Stephane Nicoll
 * @author Camille Vienot
 * @since 2.1.0
 */
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {

	/**
	 * Bean name of the application {@link TaskExecutor}.
	 */
	public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

	@Bean
	@ConditionalOnMissingBean
	public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties,
			ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
			ObjectProvider<TaskDecorator> taskDecorator) {
		TaskExecutionProperties.Pool pool = properties.getPool();
		TaskExecutorBuilder builder = new TaskExecutorBuilder();
		builder = builder.queueCapacity(pool.getQueueCapacity());
		builder = builder.corePoolSize(pool.getCoreSize());
		builder = builder.maxPoolSize(pool.getMaxSize());
		builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
		builder = builder.keepAlive(pool.getKeepAlive());
		Shutdown shutdown = properties.getShutdown();
		builder = builder.awaitTermination(shutdown.isAwaitTermination());
		builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
		builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
		builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator);
		builder = builder.taskDecorator(taskDecorator.getIfUnique());
		return builder;
	}

	@Lazy
	@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
			AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
	@ConditionalOnMissingBean(Executor.class)
	public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
		return builder.build();
	}

}

使用用线程池去执行任务:
AsyncExecutionAspectSupport.java

@Override
	@Nullable
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
		final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

		AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
		if (executor == null) {
			throw new IllegalStateException(
					"No executor specified and no default executor set on AsyncExecutionInterceptor either");
		}

		Callable<Object> task = () -> {
			try {
				Object result = invocation.proceed();
				if (result instanceof Future) {
					return ((Future<?>) result).get();
				}
			}
			catch (ExecutionException ex) {
				handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
			}
			catch (Throwable ex) {
				handleError(ex, userDeclaredMethod, invocation.getArguments());
			}
			return null;
		};

		return doSubmit(task, executor, invocation.getMethod().getReturnType());
	}

获取线程池,先看注解的value是否有值,有值则根据bean 那么获取线程池,否则从默认的线程池中获取。

@Nullable
	protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
		AsyncTaskExecutor executor = this.executors.get(method);
		if (executor == null) {
			Executor targetExecutor;
			String qualifier = getExecutorQualifier(method);
			if (StringUtils.hasLength(qualifier)) {
				targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
			}
			else {
				targetExecutor = this.defaultExecutor.get();
			}
			if (targetExecutor == null) {
				return null;
			}
			executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
					(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
			this.executors.put(method, executor);
		}
		return executor;
	}

3.自定义@Async 的线程池

自定义@Async 的线程池有两种实现方式,第一种是自定义一个线程池,然后写在注解里 @Async(value = "asyncMarkValueExecutor");第二种是实现AsyncConfigurer接口,重写getAsyncExecutor()方法。

3.1 第一种方式

1.定义一个线程池,注入spring

    @Bean(name = "asyncMarkValueExecutor")
    public Executor asyncMarkValueExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(1000);
        executor.setQueueCapacity(10000);
        executor.setThreadNamePrefix("async-thread-pool-");
        return executor;
    }

2.将线程池的bean name加到注解里。

    @Async(value = "asyncMarkValueExecutor")
    public void test(){
        try {
            TimeUnit.SECONDS.sleep(1);
            log.info(Thread.currentThread().getName()+"AsyncService");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

3.2 第二种方式

实现AsyncConfigurer接口,重写getAsyncExecutor()方法。

@Configuration
public class MyConfig implements AsyncConfigurer {

    @Bean(name = "asyncExecutor")
    public Executor asyncExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(2000);
        executor.setQueueCapacity(10000);
        executor.setThreadNamePrefix("my-thread-pool-");
        return executor;
    }

    @Override
    public Executor getAsyncExecutor() {
        return asyncExecutor();
    }
}

4.ThreadPoolTaskExecutor和ThreadPoolExecutor的区别

1. ThreadPoolTaskExecutor 是 Spring 框架提供的一个线程池实现,它是对 JDK 标准库中的 ThreadPoolExecutor 进行了封装和扩展。

2. ThreadPoolTaskExecutor比ThreadPoolExecutor更加方便使用和灵活。

2.1 ThreadPoolTaskExecutor有默认的参数,使用的时可以直接使用默认参数也可以设置参数。而ThreadPoolExecutor 需要显示的指定参数才能创建。
2.2 ThreadPoolTaskExecutor可以直接设置线程名称的前缀,以及是否为守护线程;
ThreadPoolExecutor则需要实例化一个ThreadFactory重写newThread来指定线程创建时候的属性。

ThreadPoolExecutor 示例:

ThreadFactory factory = new ThreadFactory() {
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setName("my-thread");
        return thread;
    }
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(
        5, // 核心线程数
        10, // 最大线程数
        60, // 线程空闲时间
        TimeUnit.SECONDS, // 时间单位
        new LinkedBlockingQueue<>(100), // 任务队列
        factory, // 线程工厂
        new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

3. ThreadPoolTaskExecutor与spring更加的契合。ThreadPoolTaskExecutor注入spring后,会被spring管理整个生命周期,包括初始化和线程池的销毁。

ThreadPoolTaskExecutor 会在属性填充完之后进行初始化,无需手动调用初始化方法,在bean销毁的时候会调用关闭线程池的方法:

ExecutorConfigurationSupport

public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory
		implements BeanNameAware, InitializingBean, DisposableBean {

......
// 初始化线程池
	@Override
	public void afterPropertiesSet() {
		initialize();
	}

	/**
	 * Set up the ExecutorService.
	 */
	public void initialize() {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
		}
		if (!this.threadNamePrefixSet && this.beanName != null) {
			setThreadNamePrefix(this.beanName + "-");
		}
		this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler);
	}
	....
	protected abstract ExecutorService initializeExecutor(
			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler);
...
// 销毁线程池
	@Override
	public void destroy() {
		shutdown();
	}

	/**
	 * Perform a shutdown on the underlying ExecutorService.
	 * @see java.util.concurrent.ExecutorService#shutdown()
	 * @see java.util.concurrent.ExecutorService#shutdownNow()
	 */
	public void shutdown() {
		if (logger.isDebugEnabled()) {
			logger.debug("Shutting down ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));
		}
		if (this.executor != null) {
		// 是否执行完等待中的任务,默认值为false
			if (this.waitForTasksToCompleteOnShutdown) {
				this.executor.shutdown();
			}
			// 取消线程池等待队列中的任务
			else {
				for (Runnable remainingTask : this.executor.shutdownNow()) {
					cancelRemainingTask(remainingTask);
				}
			}
			awaitTerminationIfNecessary(this.executor);
		}
	}
	...
		protected void cancelRemainingTask(Runnable task) {
		if (task instanceof Future) {
			((Future<?>) task).cancel(true);
		}
	}
	...
}	

ThreadPoolTaskExecutor

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
		implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
		...
		// 初始化线程池
			@Override
	protected ExecutorService initializeExecutor(
			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

		BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);

		ThreadPoolExecutor executor;
		if (this.taskDecorator != null) {
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler) {
				@Override
				public void execute(Runnable command) {
					Runnable decorated = taskDecorator.decorate(command);
					if (decorated != command) {
						decoratedTaskMap.put(decorated, command);
					}
					super.execute(decorated);
				}
			};
		}
		else {
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler);

		}

		if (this.allowCoreThreadTimeOut) {
			executor.allowCoreThreadTimeOut(true);
		}

		this.threadPoolExecutor = executor;
		return executor;
	}
	...
	// 取消任务的执行
		@Override
	protected void cancelRemainingTask(Runnable task) {
		super.cancelRemainingTask(task);
		// Cancel associated user-level Future handle as well
		Object original = this.decoratedTaskMap.get(task);
		if (original instanceof Future) {
			((Future<?>) original).cancel(true);
		}
	}
}

4. ThreadPoolTaskExecutor中的设置线程池有些属性的方法是线程安全的。

ThreadPoolTaskExecutor中设置线程池属性:corePoolSize、maxPoolSize、keepAliveSeconds这三个属性的方法加了synchronized关键字,而ThreadPoolExecutor则没有加。

	public void setCorePoolSize(int corePoolSize) {
		synchronized (this.poolSizeMonitor) {
			if (this.threadPoolExecutor != null) {
				this.threadPoolExecutor.setCorePoolSize(corePoolSize);
			}
			this.corePoolSize = corePoolSize;
		}
	}

5.线程池的预热

ThreadPoolExecutor的预热,可以调用两个方法:

1.prestartCoreThread 启动一个核心线程

    public boolean prestartCoreThread() {
        return workerCountOf(ctl.get()) < corePoolSize &&
            addWorker(null, true);
    }

2.prestartAllCoreThreads 启动所有的核心线程

   public int prestartAllCoreThreads() {
        int n = 0;
        while (addWorker(null, true))
            ++n;
        return n;
    }

ThreadPoolTaskExecutor如何预热?
1.因为ThreadPoolTaskExecutor是对ThreadPoolExecutor的封装,所以ThreadPoolTaskExecutor的预热需要调用ThreadPoolExecutor的方法进行预热。
2.又因为ThreadPoolTaskExecutor是在属性填充完成后才进行初始化的,所以我们需要等待ThreadPoolTaskExecutor初始化完成之后才能获取ThreadPoolExecutor ,再调用预热的方法。

重写afterPropertiesSet()方法,在初始化完成后获取ThreadPoolExecutor 进行预热:

@Configuration
public class MyConfig {

    @Bean(name = "asyncExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(){
            @Override
            public void afterPropertiesSet() {
                super.afterPropertiesSet();
              ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
              threadPoolExecutor.prestartAllCoreThreads();
                int alive = threadPoolExecutor.getPoolSize();
                System.out.println("启动线程数量:" + alive);
            }
        };
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(2000);
        executor.setQueueCapacity(10000);
        executor.setThreadNamePrefix("my-thread-pool-");
        return executor;
    }
}

输出:

启动线程数量:5
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值