El uso del grupo de subprocesos en SpringBoot

1. Inicio rápido

1. Método asincrónico

En Spring Boot, es muy simple implementar un método de ejecución asincrónica: solo necesita agregar anotaciones al proyecto (generalmente agregadas a la clase de inicio o clase de configuración del grupo de subprocesos) y agregar anotaciones @EnableAsynca los métodos que requieren ejecución asincrónica .@Async

Agregue anotaciones a la clase de inicio @EnableAsync:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableAsync
@EnableScheduling
@EnableTransactionManagement
@SpringBootApplication
public class RedisApplication {

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

}

Cree una interfaz de servicio:

public interface AsyncService {
    void simpleAsync();
}

@AsyncImplemente la interfaz y sus métodos, y agregue anotaciones asincrónicas  a los métodos :

import com.redis.service.AsyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {
    @Override
    @Async
    public void simpleAsync() {
        log.info("ThreadName【{}】异步方法开始……", Thread.currentThread().getName());
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("ThreadName【{}】异步方法结束。", Thread.currentThread().getName());
    }
}

Por lo tanto, se implementó un método de ejecución asincrónica y se escribió una prueba unitaria para realizar pruebas: 

@GetMapping("/selectAll")
    public HashMap<String, Student> selectAll(){
        List<Student> students = studentMapper.selectList(null);
        HashMap<String,Student> map=new HashMap<>();
        for (Student student : students) {
            map.put(student.getSId(),student);
        }

        asyncService.simpleAsync();;
        asyncService.simpleAsync();
        // 等待一段时间让异步方法执行结束
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return map;
    }

Ejecute la prueba unitaria y verá que dos métodos asincrónicos con diferentes nombres de subprocesos se ejecutan alternativamente:

2. Grupo de subprocesos personalizado 

El método asincrónico anterior se implementa a través del ejecutor de tareas del grupo de subprocesos inyectado automáticamente en SpringBoot. No hemos creado ningún grupo de subprocesos. En la mayoría de los casos, el grupo de subprocesos inyectado automáticamente no satisface las necesidades reales y el grupo de subprocesos debe personalizarse de acuerdo con el escenario real.

Cree una clase de configuración del grupo de subprocesos ThreadPoolConfig en el directorio de configuración del proyecto y escriba un método para crear e inicializar un objeto ejecutor de tareas del grupo de subprocesos:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class ThreadPoolConfig {
    @Bean(name = "threadPool_1")
    public ThreadPoolTaskExecutor threadPool01() {
        // 创建线程池任务执行器对象
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数量
        executor.setCorePoolSize(8);
        // 设置最大线程数量
        executor.setMaxPoolSize(16);
        // 设置阻塞队列容量
        executor.setQueueCapacity(256);
        // 设置线程空闲时间,默认为 60 秒
        executor.setKeepAliveSeconds(60);
        // 设置是否支持回收核心线程,默认为 false
        executor.setAllowCoreThreadTimeOut(false);
        // 设置线程名称前缀,若不设置则根据对象的 beanName 生成
        executor.setThreadNamePrefix("threadPool_1_");
        // 设置线程池拒绝策略,默认为 AbortPolicy,即线程数量达到最大线程数量,且阻塞队列容量已满,再添加任务则抛出异常。
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}

@AsyncEspecifique el nombre del ejecutor de la tarea del grupo de subprocesos en la anotación para utilizar el grupo de subprocesos para ejecutar tareas asincrónicas  :

import com.redis.service.AsyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {
    @Override
    @Async(value = "threadPool_1")
    public void simpleAsync() {
        log.info("ThreadName【{}】异步方法开始……", Thread.currentThread().getName());
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("ThreadName【{}】异步方法结束。", Thread.currentThread().getName());
    }
}

Después de especificar el grupo de subprocesos, vuelva a ejecutar la prueba unitaria y descubra que el prefijo del nombre del subproceso es el valor establecido en la configuración, lo que indica que se utiliza un grupo de subprocesos personalizado para realizar tareas asincrónicas: 

3. Cosas a tener en cuenta

  • @AsyncLa anotación es un método que ejecuta de forma asincrónica la modificación de la anotación a través del proxy de aspecto. Por lo tanto, la asincrónica solo se puede lograr inyectando el objeto Bean y llamándolo directamente. Las llamadas asincrónicas no se pueden lograr dentro de la clase .
  • SpringBoot inyectará automáticamente el objeto ejecutor de tareas del grupo de subprocesos solo cuando no se inyecte ningún objeto ejecutor. Siempre que cualquier objeto ejecutor de tareas del grupo de subprocesos se inyecte manualmente, SpringBoot ya no lo inyectará automáticamente.
  • Al inyectar manualmente varios objetos ejecutores de tareas del grupo de subprocesos, si @Asyncla anotación no especifica un nombre, se seleccionará aleatoriamente un objeto ejecutor de tareas del grupo de subprocesos para ejecutar la tarea asincrónica, por lo que es @Asyncmejor especificar un nombre al agregar anotaciones o ejecutarlo. en una tarea de grupo de subprocesos Si agrega anotaciones al inyectar el objeto host @Primary, todas las tareas asincrónicas sin nombres especificados se ejecutarán a través de este grupo de subprocesos.
  • import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.ThreadPoolExecutor;
    
    @Configuration
    public class ThreadPoolConfig {
        @Primary
        @Bean(name = "threadPool_1")
        public ThreadPoolTaskExecutor threadPool01() {
            // 创建线程池任务执行器对象
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            // 设置核心线程数量
            executor.setCorePoolSize(8);
            // 设置最大线程数量
            executor.setMaxPoolSize(16);
            // 设置阻塞队列容量
            executor.setQueueCapacity(256);
            // 设置线程空闲时间,默认为 60 秒
            executor.setKeepAliveSeconds(60);
            // 设置是否支持回收核心线程,默认为 false
            executor.setAllowCoreThreadTimeOut(false);
            // 设置线程名称前缀,若不设置则根据对象的 beanName 生成
            executor.setThreadNamePrefix("threadPool_1_");
            // 设置线程池拒绝策略,默认为 AbortPolicy,即线程数量达到最大线程数量,且阻塞队列容量已满,再添加任务则抛出异常。
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
            // 初始化
            executor.initialize();
            return executor;
        }
    }

2. Parámetros del grupo de subprocesos ThreadPoolTaskExecutor 

Al personalizar el grupo de subprocesos, puede configurar diferentes parámetros del grupo de subprocesos según diferentes escenarios comerciales para lograr diferentes efectos.

1. Número de subprocesos centrales

El número fijo de subprocesos en el grupo de subprocesos. Si el número de subprocesos que se ejecutan actualmente en el grupo de subprocesos es menor que el número de subprocesos principales, cuando se agrega una tarea asincrónica, un subproceso ejecutará la tarea inmediatamente. El número predeterminado de subprocesos principales de ThreadPoolTaskExecutor es 1 .
ThreadPoolTaskExecutor establece el número de subprocesos principales:

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

ThreadPoolTaskExecutor obtiene la cantidad de subprocesos principales:

public int getCorePoolSize() {
    synchronized(this.poolSizeMonitor) {
        return this.corePoolSize;
    }
}

2. Capacidad de cola

Cuando la cantidad de subprocesos activos en el grupo de subprocesos alcanza la cantidad de subprocesos principales y continúa agregando tareas asincrónicas, las nuevas tareas ingresarán a la cola de bloqueo. La capacidad de la cola es la cantidad de tareas que se pueden acomodar en la cola de bloqueo. La capacidad de cola predeterminada de ThreadPoolTaskExecutor es 2147483647 (valor máximo int) .
ThreadPoolTaskExecutor establece la capacidad de la cola:

public void setQueueCapacity(int queueCapacity) {
    this.queueCapacity = queueCapacity;
}

A través de createQueue en ThreadPoolTaskExecutor, podemos saber que cuando la queueCapacity entrante es mayor que 0, la cola de bloqueo es un objeto LinkedBlockingQueue con una capacidad específica; de lo contrario, es un objeto SynchronousQueue.

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

 

 3. Número máximo de subprocesos

Cuando la cantidad de subprocesos activos en el grupo de subprocesos alcanza la cantidad de subprocesos principales y la capacidad de la cola de bloqueo está llena, si continúa agregando tareas asincrónicas, si la cantidad de subprocesos activos no ha alcanzado la cantidad máxima de subprocesos, se generarán nuevos subprocesos. se creará para ejecutar las tareas asincrónicas en la cola. El número máximo predeterminado de subprocesos de ThreadPoolTaskExecutor es 2147483647 (valor máximo int) .
ThreadPoolTaskExecutor establece el número máximo de subprocesos:

public void setMaxPoolSize(int maxPoolSize) {
    synchronized(this.poolSizeMonitor) {
        this.maxPoolSize = maxPoolSize;
        if (this.threadPoolExecutor != null) {
            this.threadPoolExecutor.setMaximumPoolSize(maxPoolSize);
        }
    }
}

ThreadPoolTaskExecutor obtiene el número máximo de subprocesos:

public int getMaxPoolSize() {
   synchronized(this.poolSizeMonitor) {
       return this.maxPoolSize;
   }
}

4. Tiempo de inactividad del hilo

El tiempo que necesitan los subprocesos fuera del subproceso principal para sobrevivir cuando están inactivos. El tiempo de supervivencia predeterminado es 60 segundos .
ThreadPoolTaskExecutor establece el tiempo de inactividad del subproceso:

public void setKeepAliveSeconds(int keepAliveSeconds) {
    synchronized(this.poolSizeMonitor) {
        this.keepAliveSeconds = keepAliveSeconds;
        if (this.threadPoolExecutor != null) {
            this.threadPoolExecutor.setKeepAliveTime((long)keepAliveSeconds, TimeUnit.SECONDS);
        }
    }
}

ThreadPoolTaskExecutor obtiene el tiempo de inactividad del subproceso:

public int getKeepAliveSeconds() {
    synchronized(this.poolSizeMonitor) {
        return this.keepAliveSeconds;
    }
}

5. ¿Está permitido destruir subprocesos centrales?

ThreadPoolTaskExecutor no permite que el subproceso principal se destruya de forma predeterminada, es decir, el subproceso principal no se destruirá cuando su tiempo de inactividad alcance el valor establecido. Se puede configurar mediante los siguientes métodos:

public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
    this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
}

6. Prefijo del nombre del hilo

public void setThreadNamePrefix(@Nullable String threadNamePrefix) {
    super.setThreadNamePrefix(threadNamePrefix);
    this.threadNamePrefixSet = true;
}

7. Estrategia de rechazo

Cuando la cantidad de tareas asincrónicas en el grupo de subprocesos alcanza la capacidad de la cola de bloqueo y la cantidad de subprocesos activos alcanza la cantidad máxima de subprocesos, continuar agregando tareas asincrónicas activará la política de rechazo.
Cuatro estrategias de rechazo:

  • AbortPloicy ( predeterminado ): lanza una excepción
  • CallerRunsPolicy: las tareas las ejecuta la persona que llama
  • DiscardPolicy: Descarta el exceso de tareas
  • DiscardOldestPolicy: descarta la tarea más antigua en la cola de bloqueo

ThreadPoolTaskExecutor establece la política de denegación:

public void setRejectedExecutionHandler(@Nullable RejectedExecutionHandler rejectedExecutionHandler) {
    this.rejectedExecutionHandler = (RejectedExecutionHandler)(rejectedExecutionHandler != null ? rejectedExecutionHandler : new AbortPolicy());
}

Supongo que te gusta

Origin blog.csdn.net/weixin_55127182/article/details/132405990
Recomendado
Clasificación