Введение
всем привет.
В последнее время я разбираюсь с серией точек знаний DDD, и группа также занимается преобразованием и миграцией DDD.
Блог серии DDD
Разбирая код старой бизнес-логики, я обнаружил, что на нем помечено множество реализаций интерфейса@Asyncаннотация. Я сам меньше пользуюсь этой аннотацией и привык к настройке асинхронной логики.ThreadPoolExecutorИнструменты. Просто разобравшись со структурой кода на этот раз, давайте посмотрим@AsyncЧто, черт возьми, делает эта аннотация?
Мой любимый класс инструментов пула потоков:Я использовал этот пул потоков, чтобы завоевать сердца бэкэнд-девушек.
Эта статья даст вам@AsyncНачните с уровня аннотаций, чтобы шаг за шагом интерпретировать исходный код, анализировать различные методы пошаговой обработки и расширятьотслеживание ссылок сыщикаипеременная потокаКак применить фантазии.
Если в статье не указано иное, версия springboot подлежит обновлению 2.3.10.RELEASE.
2. Первые пользователи используют
Весной, будучи@AsyncАннотированные методы называются асинхронными методами. Когда эти методы выполняются, они будут выполняться в отдельном потоке, и вызывающая сторона может продолжать другие операции, не дожидаясь их завершения, что является методом асинхронного вызова по умолчанию, предоставляемым Spring.
2.1 Как пользоваться
использовать@AsyncСпособ выполнения асинхронных преобразований особенно прост.
- существует
启动类
или能被启动类扫描到的配置类
делать разметку@EnableAsync
- существует
被spring管理的bean
метод标注@Async()
-
调用方法
и被调用方法
не в той же фасолисередина.
Внимательно попробуйте три вышеупомянутых ограничения, если какое-либо из них не будет выполнено, это приведет к@Асинхронизация не работает.
2.2 Самая простая демонстрация
Определение класса запуска
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
определение метода уровня контроллера
@RestController
@RequestMapping
public class TestController {
@Autowired
TestService testService;
@GetMapping()
public void test(){
for (int i = 0; i <5 ; i++) {
testService.testAsync();
}
}
}
определение метода обслуживания
@Service
@Slf4j
public class TestServiceImpl implements TestService {
@Override
@Async
public void testAsync(){
log.info("嘻嘻");
}
}
вывод журнала
- 2021-09-15 19:39:54.300,[http-nio-8088-exec-5], com.examp.controller.TestController - 嘿嘿
- 2021-09-15 19:36:54.302,[task-5], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-4], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-1], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-2], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 19:36:54.302,[task-3], com.examp.service.impl.TestServiceImpl - 嘻嘻
Его можно найти в журнале печати,controller方法打印
иservice层方法
Журнал распечатывается с помощью不同的线程
.
Это действительно легко использовать!
2.3. Зайти в яму, чтобы задать вопросы
Из первых двух пунктов видно, что мы используем@Async
Сделать асинхронные изменения действительно просто, но есть и различные ямки в засаде.
Выбрасывая вопрос первым, вы можете сначала подумать об этом:
- Почему Али не рекомендует использовать @Async напрямую
- Является ли метод, отмеченный @Async, согласованным с транзакциями.
- В том же классе A-> B метод B помечен @Async Чтобы сделать вызов успешным, может ли асинхронный вызов быть успешным путем внедрения текущего класса в класс?
- Могут ли методы с аннотациями @Async читать переменные ThreadLocal?
- Может ли метод, отмеченный @Async, получить возвращаемое значение?
- Можно ли отследить traceId трассировки ссылки сыщика до пула потоков?
3. Анализ исходного кода
Без лишних слов, посмотрите на исходный код.
3.1.@Async
/**
* 该注解可以标记一个异步执行的方法,也可以用来标注类,表示类中的所有方法都是异步执行的。
* 入参随意,但返回值只能是void或者Future.(ListenableFuture接口/CompletableFuture类)
* Future是代理返回的切实的异步返回,用以追踪异步方法的返回值。当然也可以使用AsyncResult类(实现
* ListenableFuture接口)(Spring或者EJB都有)或者CompletableFuture类
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
//用以限定执行方法的执行器名称(自定义):Executor或者TaskExecutor
//加在类上表示整个类都使用,加在方法上会覆盖类上的设置
String value() default "";
}
3.2.@EnableAsync
/**
* 开启spring异步执行器,类似xml中的task标签配置,需要联合@Configuration注解一起使用,对应文章开头,注解需* 要标注在启动类或者能被启动类扫描到的配置类上。
*
* 默认情况下spring会先搜索TaskExecutor类型的bean或者名字为taskExecutor的Executor类型的bean,都不存在使* 用SimpleAsyncTaskExecutor执行器
*
* 可实现AsyncConfigurer接口复写getAsyncExecutor获取异步执行器,getAsyncUncaughtExceptionHandler获* 取异步未捕获异常处理器
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
// 该属性用来支持用户自定义异步注解,默认扫描spring的@Async和EJB3.1的@code @javax.ejb.Asynchronous
Class<? extends Annotation> annotation() default Annotation.class;
//标明是否需要创建CGLIB子类代理,AdviceMode=PROXY时才适用。注意设置为true时,其它spring管理的bean也会升级到CGLIB子类代理
boolean proxyTargetClass() default false;
//标明异步通知将会如何实现,默认PROXY,如需支持同一个类中非异步方法调用另一个异步方法,需要设置为ASPECTJ
AdviceMode mode() default AdviceMode.PROXY;
//标明异步注解bean处理器应该遵循的执行顺序,默认最低的优先级(Integer.MAX_VALUE,值越小优先级越高)
int order() default Ordered.LOWEST_PRECEDENCE;
}
3.3.AsyncConfigurationSelector
@EnableAsyncAnnotation — это очень очевидная аннотация запуска.Почти все аннотации классов начальной конфигурации Spring начинаются с@Enable
начало. Метод также является знакомым старым другом@Import
Для аннотаций @Import, которые не очень понятны, вы можете прочитать это:Прошлая и настоящая жизнь @SpringbootApplication в одной статье
//查询器:基于@EanableAsync中定义的模式AdviceMode加在@Configuration标记的类上,确定抽象异步配置类的实现
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
//根据当前注解标注的方法坐在类的代理方式决定代理模式
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY://jdk接口代理
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ://cglib代理
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
Класс относительно прост, он должен решить, какой режим прокси использовать.
3.4.ProxyAsyncConfiguration
Возьмите в качестве примера прокси интерфейса jdk, посмотрите
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
//判断注解元数据信息是否拿到
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
//新建一个异步注解bean后处理器
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
//配置执行器与异常处理器
bpp.configure(this.executor, this.exceptionHandler);
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
//设置是否升级到CGLIB子类代理,默认不开启
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
//设置执行优先级,默认最后执行
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}
}
Операция класса конфигурации также относительно ясна, то есть интеграцияAbstractAsyncConfigurationабстрактный класс, новыйAsyncAnnotationBeanPostProcessorПостпроцессор и инициализация параметров.
3.5.AbstractAsyncConfiguration
@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
//enableAsync注解属性
@Nullable
protected AnnotationAttributes enableAsync;
//线程执行器
@Nullable
protected Supplier<Executor> executor;
//异常执行器
@Nullable
protected Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;
//设置注解元数据信息
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
}
}
//根据配置设置异步任务执行器和异常处理器
@Autowired(required = false)
void setConfigurers(Collection<AsyncConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException("Only one AsyncConfigurer may exist");
}
AsyncConfigurer configurer = configurers.iterator().next();
this.executor = configurer::getAsyncExecutor;
this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
}
}
3.6.AsyncAnnotationBeanPostProcessor
Как видно из графика зависимостей, это постпроцессор бина
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
//省略部分代码
//初始化异步处理切面
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
//初始化切面
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
}
3.7.AsyncAnnotationAdvisor
Вуху, я узнал, что это аспект асинхронной аннотации, которая определяет аспект разбора, анализируемого @Async.Конкретная обработка находится вAbstractAdvisingBeanPostProcessor.postProcessAfterInitializationметод
3.8 Выполнение аспекта перехватчика
Метод вызова интерфейса Interceptor,
Точка останова попадает в метод интерфейса, шаг за шагом, найдитеAsyncExecutionInterceptorсвоего рода
@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();
//如果代理方法的返回值是Future,则阻塞等待执行完毕得到结果
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());
}
Посмотрите на последний основной методdoSubmit
@Nullable
protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
//返回值是CompletableFuture处理
if (CompletableFuture.class.isAssignableFrom(returnType)) {
return CompletableFuture.supplyAsync(() -> {
try {
return task.call();
}
catch (Throwable ex) {
throw new CompletionException(ex);
}
}, executor);
}
//返回值是ListenableFuture处理
else if (ListenableFuture.class.isAssignableFrom(returnType)) {
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
}
//返回值是ListenableFuture处理
else if (Future.class.isAssignableFrom(returnType)) {
return executor.submit(task);
}
//其他情况(无参或者非以上返回参数,例如方法返回参数是string)
else {
//断点处
executor.submit(task);
return null;
}
}
Снова запустите демонстрационный метод, сломав точку в приведенной выше строке кода.
Задача обнаружения была отправлена наapplicationTaskExecutorисполнитель задачи
Параметры пула потоков:
Количество основных потоков: 8
Размер очереди и максимальный пул потоков равны Interger.Max.
3.9. Резюме
Инициализировать класс аспекта, который анализирует аннотацию @Async.
Parse @Async, pointcut переплетает выполнение
4. Обзор проблемы
4.1 Почему Али не рекомендует использовать @Async напрямую
Вы можете найти его в конце 2.8, по умолчанию,@Async
Соответствующая конфигурация пула потоков最大线程数
и最大队列数
за2147483647
.
То есть, если отмечено@Async
метод, параллелизм события внезапно увеличивается, и нагрузка на систему будет увеличиваться мгновенно, и машина будет остановлена, если она преувеличена.
Значит, нельзя использовать аннотацию @Async?
Ответ определенно否定的
.
существует@EnableAsync
В комментарии к классу аннотации уже говорилось, что bean-компоненту пользовательского пула потоков разрешено заменять системный пул потоков по умолчанию.
поискapplicationTaskExecutor,оказатьсяTaskExecutionAutoConfiguration
@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;
}
//系统中不存在Executor的bean时,默认加载名为:applicationTaskExecutor的bean。这里的bean即为实际切面执行时,从beanfactory里面获取的beanName叫做taskExecutor的线程池进行执行异步任务
@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();
}
}
Поэтому, если вы хотите использовать @Async, обязательно добавьте自定义线程池
возвращенный боб
Например
@Configuration
public class ThreadPoolTaskConfig {
/** 核心线程数(默认线程数) */
private static final int CORE_POOL_SIZE = 5;
/** 最大线程数 */
private static final int MAX_POOL_SIZE = 10;
/** 允许线程空闲时间(单位:默认为秒) */
private static final int KEEP_ALIVE_TIME = 10;
/** 缓冲队列大小 */
private static final int QUEUE_CAPACITY = 200;
/** 线程池名前缀 */
private static final String THREAD_NAME_PREFIX = "Async-Service-";
@Bean
public ThreadPoolTaskExecutor taskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(KEEP_ALIVE_TIME);
executor.setKeepAliveSeconds(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
Здесь также следует отметить, что когда ваша весенняя версия является версией до 2.1, нет пула потоков с конфигурацией по умолчанию applicationTaskExecutor, но SimpleAsyncTaskExecutor, Его метод отправки заключается в постоянном создании новых потоков для выполнения задач, а также потребляет ресурсы , поэтому не рекомендуется использовать его здесь напрямую.
4.2 Согласен ли метод, отмеченный транзакцией @Async?
Из анализа видно, что@Async
Класс, в котором находится аннотированный метод, проксируется и выполняется с использованием потока пула потоков.
Например, теперь вызовите цепочку:
UserService.test1()-> UserService.test2()-> UserService.test3() и UserRoleService.test1()
Метод UserService.test()2 помечен@Async
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
UserRoleService userRoleService;
@Override
@Transactional(rollbackFor = Exception.class)
public void test3(){
//省略save逻辑
save();
}
/**
* 事务一致
*/
@Override
@Async
@Transactional(rollbackFor = Exception.class)
public void test2(){
test3();
userRoleService.test1();
}
/**
* 事务不一致
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void test1(){
//省略save逻辑
save();
test2();
}
}
@Service
public class UserRoleServiceImpl implements UserRoleService {
@Override
@Transactional(rollbackFor = Exception.class)
public void test1(){
//省略save逻辑
save();
throw new RuntimeException("1");
}
}
Хахаха, если сделать так, чтобы код не сообщал об ошибках, то можно управлять транзакцией как угодно.Все равно БД будет успешно сброшена, и ее не откатят.Неважно, есть ли транзакция. Конечно, это ерунда, код не сообщает об ошибках, и транзакция не контролируется, это невозможно для производства.
На самом деле это事务管理跨了线程,那么不同线程的事务就不能保证一致了。
4.3. В том же классе, A->B, метод B помечен @Async Чтобы сделать вызов успешным, может ли асинхронный вызов быть успешным путем внедрения текущего класса в класс?
Давайте сначала посмотрим на тестовый код
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
UserService userService;
@Override
public void testAsync(){
userService.testAsync1();
}
@Override
@Async()
public void testAsync1(){
log.info("嘻嘻1");
}
}
Мы знаем, что spring помогает нам решать циклические зависимости по умолчанию.
Круговая зависимость: внедрение класса свойств B в класс A, внедрение класса A в свойство класса B; внедрение класса A в себя называется циклической зависимостью. Трехуровневый кеш, используемый spring по умолчанию, решает эту ситуацию за нас. Если вас интересует кеш третьего уровня, вы можете посмотреть анализ на Baidu, я также найду время, чтобы написать аналитическую статью в будущем.
Запустим его и обнаружим, что запуск не удался, а ошибка следующая
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userServiceImpl': Bean with name 'userServiceImpl' has been injected into other beans [userServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
значит это случилоськруговая зависимость,впадать вбесконечный цикл,не удалось активировать!
Эй, разве вышеизложенное не говорило, что Spring решил для нас круговые зависимости? Почему произошел сбой запуска после добавления аннотации @Async?
Учитывая, что некоторые читатели мало что знают о кеше L3, текстовый вариант заключения приводится непосредственно здесь.Если вам интересно, вы можете перейти к приведенному выше исходному коду для отладочного анализа.
в заключении:
Когда UserService помещается в кеш второго уровня, прокси-сервер будет проверен, но генерация прокси-сервера должна определить, является ли соответствующий тип постпроцессора:SmartInstantiationAwareBeanPostProcessor, как вы можете видеть в 2.6 выше, постпроцессор, проанализированный @AsyncAsyncAnnotationBeanPostProcessorПростоBeanPostProcessortype, поэтому ранний конструктор предоставляет исходный объект. Поэтому UserService связан с самим собой и является исходным объектом. но позжеAsyncAnnotationBeanPostProcessorПостпроцессор будетUserServiceОбернут в прокси-объект. Поэтому в настоящее время класс находится в логике окончательной проверки.UserService
Внутренние свойства зависят отuserService
Фактический компонент, не равный прокси-объекту. Войдите в программу самопроверки более низкого уровня и, наконец, сообщите об ошибке.
Здесь даже стартап запустить нельзя, и ответ, естественно, что вызов не может быть успешным.
следовательно,异步调用需要成功,则必须限制调用方法需要是两个不同bean的方法,即为@Async标注的方法必须能够被切面类代理到
.
4.4 Могут ли методы с аннотациями @Async читать переменные ThreadLocal?
По сути, @Async по-прежнему использует пул потоков, поэтому используйте его напрямую.ThreadLocal
, невозможно синхронизировать переменные потока из основного потока в потоки в пуле потоков, вам нужно использовать переменные потока с открытым исходным кодом Alibaba:TransmittableThreadLocal.
Знание Threadlocal может относиться к:Прочитайте прошлое и настоящее ThreadLocal в одной статье
4.5 Может ли метод, помеченный @Async, получить возвращаемое значение?
Из метода invoke 2.8 известно, что прямое возвращаемое значение метода получить нельзя, и будет получен null, а возвращаемое значение можно получить через класс Future.
4.6 Можно ли отследить traceId трассировки ссылки slueth до пула потоков?
Может
пом импорт
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
выполнить снова
- 2021-09-15 23:13:13.502, INFO, [7f6570e5ad87cebe,7f6570e5ad87cebe,], [http-nio-8088-exec-2], com.examp.controller.TestController - 嘿嘿
- 2021-09-15 23:13:13.517, INFO, [7f6570e5ad87cebe,6c4ef07be0f53434,7f6570e5ad87cebe], [Async-Service-2], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 23:13:13.517, INFO, [7f6570e5ad87cebe,8ff5d5ce00c93add,7f6570e5ad87cebe], [Async-Service-1], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 23:13:13.517, INFO, [7f6570e5ad87cebe,99b553b90241b79e,7f6570e5ad87cebe], [Async-Service-3], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 23:13:13.517, INFO, [7f6570e5ad87cebe,42c54ecd36df454c,7f6570e5ad87cebe], [Async-Service-4], com.examp.service.impl.TestServiceImpl - 嘻嘻
- 2021-09-15 23:13:13.517, INFO, [7f6570e5ad87cebe,176b3679aa72038e,7f6570e5ad87cebe], [Async-Service-5], com.examp.service.impl.TestServiceImpl - 嘻嘻
Все еще вернитесь к точке останова метода 2.8, чтобы просмотреть
тип становитсяLazyTraceThreadPoolTaskExecutor,ДаThreadPoolTaskExecutorподкласс
ПроверятьLazyTraceThreadPoolTaskExecutorЦепочка вызовов сообщает, что вExecutorBeanPostProcessorПостпроцессор проксирует системуThreadPoolTaskExecutorвид фасоли. То есть, по сути, это ваша привычка выполнять задания.ThreadPoolTaskExecutor,ТолькоExecutorBeanPostProcessorУлучшено отслеживание ссылок.
5. Резюме
Эта статья из@Async
Простое использование , начатое с@Async
Анализ исходного кода и, наконец,@Async
Проблемы, которые могут возникнуть в процессе использования, обобщаются и анализируются. Конечно, @Async ароматен, но не помещайте все асинхронные задачи всей системы в пул потоков. Большое количество асинхронных задач разных бизнесов должно обрабатываться отдельно от пула потоков, насколько это возможно.
6. Ссылка
Асинхронная задача Spring Анализ исходного кода аннотации @Async
7. Свяжитесь со мной
Если есть неточности в тексте, поправьте меня, текст писать непросто, ставьте лайк, ладно~
Диндин: louyanfeng25
WeChat: baiyan_lou
Общественный номер: дядя Бай Ян