1. 程式人生 > 其它 >springboot使用執行緒池

springboot使用執行緒池

一、執行緒池執行流程圖

在Springboot中對使用執行緒池其進行了簡化處理,只需要配置一個型別為java.util.concurrent.TaskExecutor或其子類的bean,並在配置類或直接在程式入口類上宣告註解@EnableAsync

呼叫非同步方法也很簡單,在由Spring管理的物件的方法上標註註解@Async,顯式呼叫即可生效。

一般使用Spring提供的ThreadPoolTaskExecutor類作為執行緒池物件。

二、springboot使用執行緒池demo

1、配置執行緒池

建立一個執行緒池的配置,讓Spring Boot載入,用來定義如何建立一個ThreadPoolTaskExecutor

,要使用@Configuration和@EnableAsync這兩個註解,表示這是個配置類,並且是執行緒池的配置類

@Configuration
@EnableAsync
public class ExecutorConfig {

    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value(
"${async.executor.thread.max_pool_size}") private int maxPoolSize; @Value("${async.executor.thread.queue_capacity}") private int queueCapacity; @Value("${async.executor.thread.name.prefix}") private String namePrefix; @Bean(name = "asyncServiceExecutor") public Executor asyncServiceExecutor() { logger.info(
"start asyncServiceExecutor"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //配置核心執行緒數 executor.setCorePoolSize(corePoolSize); //配置最大執行緒數 executor.setMaxPoolSize(maxPoolSize); //配置佇列大小 executor.setQueueCapacity(queueCapacity); //配置執行緒池中的執行緒的名稱字首 executor.setThreadNamePrefix(namePrefix); // rejection-policy:當pool已經達到max size的時候,如何處理新任務 // CALLER_RUNS:不在新執行緒中執行任務,而是有呼叫者所在的執行緒來執行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //執行初始化 executor.initialize(); return executor; } }

@Value是我配置在application.properties,可以參考配置,自由定義

# 非同步執行緒配置
# 配置核心執行緒數
async.executor.thread.core_pool_size = 5
# 配置最大執行緒數
async.executor.thread.max_pool_size = 5
# 配置佇列大小
async.executor.thread.queue_capacity = 99999
# 配置執行緒池中的執行緒的名稱字首
async.executor.thread.name.prefix = async-service-

2、建立非同步呼叫介面以及實現類

public interface AsyncService {
    /**
     * 執行非同步任務
     * 可以根據需求,自己加引數擬定,我這裡就做個測試演示
     */
    void executeAsync();
}

實現類

@Service
public class AsyncServiceImpl implements AsyncService {
    private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);

    @Override
    @Async("asyncServiceExecutor")
    public void executeAsync() {
        logger.info("start executeAsync");

        System.out.println("非同步執行緒要做的事情");
        System.out.println("可以在這裡執行批量插入等耗時的事情");

        logger.info("end executeAsync");
    }
}

@Async("asyncServiceExecutor")表示指定使用asyncServiceExecutor執行緒池非同步執行該方法

注:@Async的呼叫涉及到動態代理,如果直接將需要非同步操作的方法寫到直接業務類中,業務類直接呼叫,則執行邏輯不會走到代理類,非同步就會失效!!!

ps:非同步失效原因①非同步方法使用註解@Async的返回值只能為void或者Future。

②沒有走Spring的代理類。因為像@Transactional和@Async註解的實現都是基於Spring的AOP,而AOP的實現是基於動態代理模式實現的。那麼註解失效的原因就很明顯了,有可能因為呼叫方法的是物件本身而不是代理物件,因為沒有經過Spring容器

3、呼叫非同步方法

接下來就是在Controller裡或者是哪裡通過註解@Autowired注入這個Service

@Autowired
private AsyncService asyncService;

@GetMapping("/async")
public void async(){
    asyncService.executeAsync();
}

用postman或者其他工具來多次測試請求一下

 2018-07-16 22:15:47.655  INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
非同步執行緒要做的事情
可以在這裡執行批量插入等耗時的事情
2018-07-16 22:15:47.655  INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
2018-07-16 22:15:47.770  INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
非同步執行緒要做的事情
可以在這裡執行批量插入等耗時的事情
2018-07-16 22:15:47.770  INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
2018-07-16 22:15:47.816  INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync

三、擴充套件知識(列印執行緒池執行狀況)

通過以上日誌可以發現,[async-service-]是有多個執行緒的,顯然已經在我們配置的執行緒池中執行了,並且每次請求中,controller的起始和結束日誌都是連續列印的,表明每次請求都快速響應了,而耗時的操作都留給執行緒池中的執行緒去非同步執行;

雖然我們已經用上了執行緒池,但是還不清楚執行緒池當時的情況,有多少執行緒在執行,多少在佇列中等待呢?這裡我建立了一個ThreadPoolTaskExecutor的子類,在每次提交執行緒的時候都會將當前執行緒池的執行狀況打印出來

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (null == threadPoolExecutor) {
            return;
        }

        logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }
}

如上所示,showThreadPoolInfo方法中將任務總數、已完成數、活躍執行緒數,佇列大小都打印出來了,然後Override了父類的execute、submit等方法,在裡面呼叫showThreadPoolInfo方法,這樣每次有任務被提交到執行緒池的時候,都會將當前執行緒池的基本情況列印到日誌中;

修改ExecutorConfig.java的asyncServiceExecutor方法,將ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改為ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor()

@Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        //在這裡修改
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心執行緒數
        executor.setCorePoolSize(corePoolSize);
        //配置最大執行緒數
        executor.setMaxPoolSize(maxPoolSize);
        //配置佇列大小
        executor.setQueueCapacity(queueCapacity);
        //配置執行緒池中的執行緒的名稱字首
        executor.setThreadNamePrefix(namePrefix);

        // rejection-policy:當pool已經達到max size的時候,如何處理新任務
        // CALLER_RUNS:不在新執行緒中執行任務,而是有呼叫者所在的執行緒來執行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //執行初始化
        executor.initialize();
        return executor;
    }

再次啟動該工程測試

2018-07-16 22:23:30.951  INFO 14088 --- [nio-8087-exec-2] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [0], completedTaskCount [0], activeCount [0], queueSize [0]
2018-07-16 22:23:30.952  INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
非同步執行緒要做的事情
可以在這裡執行批量插入等耗時的事情
2018-07-16 22:23:30.953  INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
2018-07-16 22:23:31.351  INFO 14088 --- [nio-8087-exec-3] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [1], completedTaskCount [1], activeCount [0], queueSize [0]
2018-07-16 22:23:31.353  INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync
非同步執行緒要做的事情
可以在這裡執行批量插入等耗時的事情
2018-07-16 22:23:31.353  INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl   : end executeAsync
2018-07-16 22:23:31.927  INFO 14088 --- [nio-8087-exec-5] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [2], completedTaskCount [2], activeCount [0], queueSize [0]
2018-07-16 22:23:31.929  INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl   : start executeAsync

看紅色部分這說明提交任務到執行緒池的時候,呼叫的是submit(Callable task)這個方法,當前已經提交了2個任務,完成了2個,當前有0個執行緒在處理任務,還剩0個任務在佇列中等待,執行緒池的基本情況一路瞭然;

摘自:https://blog.csdn.net/m0_37701381/article/details/81072774