스프링 부트 주석 @ 비동기 스레드 풀 인스턴스 설명

in #kr-dev2 years ago

Spring 3부터 @ async 어노테이션이 제공되며 이는 비동기식으로 호출하기 위해 메소드에 표시될 수 있습니다. 호출자는 호출될 때 즉시 반환하고 메서드의 실제 실행은 스프링 작업 실행자의 작업에 제출되고 지정된 스레드 풀의 스레드에 의해 실행됩니다.

1. TaskExecutor

Spring 비동기 스레드 풀 인터페이스 클래스, 그 본질은 Java입니다. 활용 병발 사정. 집행자

스프링에 의해 구현된 예외 스레드 풀:

1. Simpleasynctaskexecutor: 실제 스레드 풀이 아닙니다. 이 클래스는 스레드를 재사용하지 않으며 호출될 때마다 새 스레드가 생성됩니다.

2. Synctaskexecutor: 이 클래스는 비동기 호출을 구현하지 않고 동기 작업일 뿐입니다. 멀티스레딩이 필요하지 않은 장소에만 해당

3. Concurrenttaskexecutor: 실행기의 어댑터 클래스, 권장하지 않음. threadpooltaskexecutor가 요구 사항을 충족하지 않으면 다음을 사용하는 것이 좋습니다.

클래스
4. SimpleThreadPool taskexecutor: 석영의 SimpleThreadPool 클래스. 스레드 풀이 석영과 비 석영 모두에서 사용되는 경우에만 사용할 수 있습니다.

5. Threadpooltaskexecutor: 가장 일반적으로 사용되며 권장됩니다. 그 본질은 Java의 패키징입니다. 활용 병발 사정. ThreadPoolExecutor

2. @EnableAsync @Async

(1) springboot 시작 클래스의 @ enableasync 주석은 비동기 호출을 가능하게 합니다.

(2) Spring은 @ Async에 대한 비동기 작업을 정의합니다.

비동기식으로 하는 세 가지 방법이 있습니다.

1. 가장 간단한 비동기식 호출, 반환 값은 void입니다. @ async 반환 값 없음 호출을 기반으로 클래스를 사용하고 메서드를 사용하는 것이 좋습니다(메소드를 사용하는 것이 좋습니다). 예외를 throw해야 하는 경우 새 예외를 수동으로 throw해야 합니다.

2. 매개변수가 있는 비동기 메서드는 비동기적으로 호출할 수 있으며 매개변수를 전달할 수 있습니다.

3. 예외 호출은 asyncuncaughtexceptionhandler에 의해 처리되지 않는 future를 반환합니다. 메서드에서 또는 호출자가 futrue.get을 호출할 때 예외를 포착하고 처리해야 합니다.

**3. @ async 애플리케이션의 기본 쓰레드 풀
**

스프링 애플리케이션의 기본 쓰레드 풀은 @ async 어노테이션을 사용할 때 쓰레드 풀의 이름을 지정하지 않는다는 뜻이다. 소스코드를 보면 @ async의 기본 쓰레드 풀은 simpleasynctaskexecutor이다.

기본 스레드 풀의 단점

스레드 풀 응용 프로그램에서 Alibaba Java 개발 사양을 참조하십시오. 스레드 풀은 실행자가 생성할 수 없으며 시스템의 기본 스레드 풀을 사용할 수 없습니다. ThreadPoolExecutor를 사용하는 것이 좋습니다. 이는 개발 엔지니어가 스레드 풀의 운영 규칙에 대해 보다 명확하게 하고 리소스 고갈의 위험을 방지할 수 있도록 합니다. 집행자 방법의 단점:

새로운 고정 스레드 풀 및 새로운 단일 스레드 실행기: 주요 문제는 누적된 요청 처리 대기열이 oom을 포함하여 많은 메모리를 소비할 수 있다는 것입니다.

Newcachedthreadpool 및 newscheduledthreadpool: 문제는 최대 스레드 수가 integer.max_ 값이라는 점입니다. 매우 많은 수의 스레드를 생성하거나 심지어 oom을 생성할 수도 있습니다.

@async의 기본 비동기 구성은 simpleasynctaskexecutor를 사용합니다. 기본적으로 태스크는 스레드 풀에 스레드를 생성합니다. 시스템에서 스레드가 계속 생성되면 시스템이 결국 너무 많은 메모리를 차지하게 되어 outofmemoryerror 오류가 발생합니다. 스레드 생성 문제를 해결하기 위해 simpleasynctaskexecutor는 전류 제한 메커니즘을 제공합니다. 스위치는 concurrencylimit 속성에 의해 제어됩니다. concurrencylimit > = 0일 때 현재 제한 메커니즘이 켜집니다. 기본적으로 현재 제한 메커니즘은 꺼져 있습니다. 즉, concurrencylimit = – 1입니다. 꺼져 있으면 작업을 처리하기 위해 새 스레드가 지속적으로 생성됩니다. 기본 구성에 따라 simpleasynctaskexecutor는 엄밀한 의미에서 스레드 풀이 아니므로 스레드 재사용 기능을 달성할 수 없습니다.

**4. @ 비동기 애플리케이션 사용자 정의 스레드 풀
**

사용자 지정 스레드 풀은 시스템에서 보다 세분화된 방식으로 스레드 풀을 제어할 수 있습니다. 쓰레드 풀의 크기 설정을 조절하는 것이 편리하며 쓰레드는 예외 제어 및 처리를 수행한다. 시스템 사용자 지정 스레드 풀이 기본 스레드 풀을 대체하도록 설정되면 여러 모드에서 설정할 수 있지만 기본 스레드 풀을 교체한 후 하나의 스레드 풀만 설정할 수 있습니다(여러 클래스가 asyncconfigurator를 상속하도록 설정할 수 없음). 사용자 지정 스레드 풀에는 다음 모드가 있습니다.

  • 인터페이스 asyncconfigurator의 재구현
  • asyncconfigurersupport 상속
  • 기본 제공 작업 실행기를 대체하도록 사용자 지정 작업 실행기 구성

@ async에 대한 스프링 소스 코드의 기본 호출 규칙을 살펴보면 먼저 asyncconfigurersupport 인터페이스를 구현하는 소스 코드의 클래스를 쿼리합니다. 단, 기본적으로 설정되어 있는 쓰레드 풀과 비동기 처리 방식은 비어 있다. 따라서 인터페이스를 상속 받든 다시 구현하든 스레드 풀을 지정해야 합니다. 그리고 public executor getasyncexecution() 메소드를 다시 구현합니다.

(1) 인터페이스 asyncconfigurer 구현

@Configuration
public class AsyncConfiguration implements AsyncConfigurer {
  @Bean("kingAsyncExecutor")
  public ThreadPoolTaskExecutor executor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    int corePoolSize = 10;
    executor.setCorePoolSize(corePoolSize);
    int maxPoolSize = 50;
    executor.setMaxPoolSize(maxPoolSize);
    int queueCapacity = 10;
    executor.setQueueCapacity(queueCapacity);
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    String threadNamePrefix = "kingDeeAsyncExecutor-";
    executor.setThreadNamePrefix(threadNamePrefix);
    executor.setWaitForTasksToCompleteOnShutdown(true);
    //Use custom cross thread request level thread factory classes
    RequestContextThreadFactory threadFactory = RequestContextThreadFactory.getDefault();
    executor.setThreadFactory(threadFactory);
    int awaitTerminationSeconds = 5;
    executor.setAwaitTerminationSeconds(awaitTerminationSeconds);
    executor.initialize();
    return executor;
  }

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

  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    Return (ex, method, params) - > errorlogger. Getinstance(). Log (string. Format ("execute asynchronous task '", method), ex.);
  }
}

(2) asyncconfigurersupport 상속

@Configuration 
@EnableAsync 
class SpringAsyncConfigurer extends AsyncConfigurerSupport { 
 
  @Bean 
  public ThreadPoolTaskExecutor asyncExecutor() { 
    ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor(); 
    threadPool.setCorePoolSize(3); 
    threadPool.setMaxPoolSize(3); 
    threadPool.setWaitForTasksToCompleteOnShutdown(true); 
    threadPool.setAwaitTerminationSeconds(60 * 15); 
    return threadPool; 
  } 
 
  @Override 
  public Executor getAsyncExecutor() { 
    return asyncExecutor; 
} 

 @Override 
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
  Return (ex, method, params) - > errorlogger. Getinstance(). Log (string. Format ("execute asynchronous task '", method), ex.);
}
}

(3) 사용자 정의 taskexecutor 구성

소스 코드에서 asyncconfigurator의 기본 스레드 풀이 비어 있기 때문에 스프링은 먼저 스레드 풀이 beanfactory를 통해 연결되었는지 확인합니다. GetBean(taskexecutor. 클래스). 설정하지 않으면 스프링은 스레드 풀이 beanfactory를 통해 연결되었는지 확인합니다. GetBean(기본값)_ TASK_ EXECUTOR_ BEAN_ 그런 다음 기본 이름이 taskexecutor인 스레드 풀이 있는지 쿼리합니다. 따라서 프로젝트에서 taskexecutor라는 빈을 정의하여 기본 스레드 풀을 생성합니다. 스레드 풀의 이름을 지정하지 않고 스레드 풀을 선언할 수도 있습니다. 기본 계층은 taskexecutor를 기반으로 합니다. 수업.

예를 들어:

집행자. 클래스: threadpoolexecutoradapter > ThreadPoolExecutor > abstractexecutorservice > executorservice > executor (이 모드에서 최종 하단 레이어는 executor입니다. 클래스. 기본 스레드 풀을 교체할 때 기본 스레드 풀 이름을 taskexecutor로 설정해야 함)

집행자. 클래스: threadpooltaskexecutor > Schedulingtaskexecutor > asynctaskexecutor > taskexecutor (이 모드에서 최하위 계층은 taskexecutor. 클래스. 기본 쓰레드 풀 교체 시 쓰레드 풀 이름을 명시하지 않을 수 있다.)

package intellif.configs;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author liuyu
 * @className TaskConfiguration
 * @date 2019/12/17 11:40
 * @description
 */


@Component
public class TaskConfiguration implements AsyncConfigurer {

  private static Logger logger = LogManager.getLogger(TaskConfiguration.class);

  @Value("${thread.pool.corePoolSize:10}")
  private int corePoolSize;

  @Value("${thread.pool.maxPoolSize:20}")
  private int maxPoolSize;

  @Value("${thread.pool.keepAliveSeconds:4}")
  private int keepAliveSeconds;

  @Value("${thread.pool.queueCapacity:512}")
  private int queueCapacity;

  @Value("${thread.pool.waitForTasksToCompleteOnShutdown:true}")
  private boolean waitForTasksToCompleteOnShutdown;

  @Value("${thread.pool.awaitTerminationSeconds:60}")
  private int awaitTerminationSeconds;


  @Override
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    //Number of core threads
    executor.setCorePoolSize(corePoolSize);
    //The maximum number of threads in the thread pool. Only when the buffer queue is full, can you apply for threads that exceed the number of core threads
    executor.setMaxPoolSize(maxPoolSize);
    //When the idle time of a thread exceeds that of a core thread, it will be destroyed after the idle time arrives
    executor.setKeepAliveSeconds(keepAliveSeconds);
    ////The queue used to buffer the execution of tasks
    executor.setQueueCapacity(queueCapacity);
    //The prefix of the thread pool name, which can be used to locate the thread pool where the processing task is located
    executor.setThreadNamePrefix("taskExecutor-");
    //The processing strategy of thread pool for rejecting tasks
    executor.setRejectedExecutionHandler((Runnable r, ThreadPoolExecutor exe) -> {
      Logger. Warn ("the current task thread pool queue is full.");
    });
    //This method is used to set when the thread pool is closed, wait for all tasks to complete, and then continue to destroy other beans, so that the destruction of these asynchronous tasks will precede the destruction of the database connection pool object.
    executor.setWaitForTasksToCompleteOnShutdown(waitForTasksToCompleteOnShutdown);
    //This method is used to set the waiting time of tasks in the thread pool. If the task has not been destroyed after this time, it will be forcibly destroyed to ensure that the application can be closed instead of blocking.
    executor.setAwaitTerminationSeconds(awaitTerminationSeconds);
    executor.initialize();
    return executor;
  }

  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    Return (ex, method, params) - > logger. Error ("unknown exception occurred in thread pool execution task.", ex));
  }
}

다중 스레드 풀

@Async 주석, 시스템 기본 또는 사용자 지정 스레드 풀(기본 스레드 풀 대신) 사용. 프로젝트에서 여러 스레드 풀을 설정할 수 있습니다. 비동기식으로 호출할 경우 @ async("new")_ task")와 같이 호출할 스레드 풀의 이름을 나타냅니다.。

**5. @ async 주석 실패 이유
**

과거에는 프록시 클래스가 없습니다. 이 클래스가 호출되면 프록시 클래스를 거치지 않고 내부적으로 직접 자신을 호출합니다.

1. @ springbootapplication 시작 클래스에 @ enableasync 주석이 추가되지 않았습니다.

2. 비동기 메서드는 @ async 주석을 사용합니다. 반환 값은 void 또는 future만 될 수 있습니다.

3. 스프링 프록시 클래스가 없습니다. @ transactional 및 @ async 주석의 구현은 spring AOP를 기반으로 하고 AOP의 구현은 동적 프록시 모드를 기반으로 하기 때문입니다. 주석 무효화의 이유는 분명합니다. 스프링 컨테이너를 통과하지 않았기 때문에 객체 자체가 프록시 객체 대신 메소드를 호출하고 있을 수 있습니다.

**분해물:
**

다음은 세 번째 상황에 대한 구체적인 솔루션입니다.

1. 주석 방법은 공개되어야 합니다.

2. 메서드는 다른 클래스에서 호출해야 합니다. 즉, 클래스 외부에서 호출하면 클래스의 내부 호출이 유효하지 않습니다.

3. 클래스 내부에서 호출해야 하는 경우 먼저 프록시 클래스를 가져와야 합니다. 아래 코드는 다음과 같습니다


@Service
public class AsyncService{
 public void methodA(){
  ...
  AsyncService asyncServiceProxy = SpringUtil.getBean(AsyncService.class);
  asyncServiceProxy .methodB();
  ...
 }
 
 @Async
 public void methodB() {
  ...
 }
}

이 클래스에서 이 클래스를 구현하기 위한 비동기 멀티스레딩 메서드를 정의할 수 있습니다.

실현해야 하는 두 가지 유형이 있습니다.

  • 공개 방법
  • 수동으로 스프링 빈 가져오기

springutils의 도구 클래스의 경우 수동으로 bean 메소드를 얻으십시오.

package intellif.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author liuyu
 * @className SpringUtils
 * @date 2019/12/16 20:55
 * @description
 */

@Component("springContextUtil")
public class SpringUtils implements ApplicationContextAware {
  private static ApplicationContext applicationContext = null;

  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }

  @SuppressWarnings("unchecked")
  public static <T> T getBean(String beanId) {
    return (T) applicationContext.getBean(beanId);
  }

  public static <T> T getBean(Class<T> requiredType) {
    return (T) applicationContext.getBean(requiredType);
  }
  /**
   *After the spring container is started, the ApplicationContext will be automatically injected into the container, and then we will add the ApplicationContext
   *Assign values to static variables to facilitate subsequent access to container objects
   * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
   */
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    SpringUtils.applicationContext = applicationContext;
  }
}

출처 : https://developpaper.com/spring-boot-annotation-async-thread-pool-instance-explanation/

Sort:  

[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.

Coin Marketplace

STEEM 0.29
TRX 0.12
JST 0.033
BTC 63464.16
ETH 3111.33
USDT 1.00
SBD 3.98