栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

8.9.3-多线程(jdk和spring线程池的配置及使用)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

8.9.3-多线程(jdk和spring线程池的配置及使用)

文章目录
  • 1. jdk线程池
    • 1.1. 超过maxPoolSize后直接丢弃策略
    • 1.2. 提交Callable对象,可以获取结果
      • 1.2.1. 非并行获取结果
      • 1.2.2. 并行获取结果
  • 2. Spring线程池
    • 2.1. 概览
    • 2.2. 配置线程池
    • 2.3. 准备使用线程池的service
    • 2.4. 准备controller
    • 2.5. 调用结果分析

源代码:https://gitee.com/kelvin11/multithreading.git
内容提要:

  1. jdk ThreadPoolExecutor参数构建线程池
    1. 线程池配置参数说明:corePoolSize、maxPoolSize、queue、RejectExecutionHandler拒绝策略。何时填充queue
    2. 提交callable任务,通过future获取结果。
    3. 提交callable任务,使用stream().parallel()并行获取结果
  2. spring ThreadPoolTaskExecutor线程池配置
    1. 注意:异步方法不能与被调用的异步方法在同一个类中,否则将不使用多线程
    2. CallerRunsPolicy 由该线程调用线程运行。直接调用Runnable的run方法运行(即,当超过maxPoolSize之后,提交的请求在利用完主线程之后,会阻塞,不抛异常)
    3. 手动注入ThreadPoolTaskExecutor,提交callable任务,通过future获取结果
1. jdk线程池

通过idea的diagram总览jdk线程池,层次结构比较简单,我们使用ExecutorService作为接口,ThreadPoolExecutor作为实现类来创建线程池。

要注意的是:推荐自己去new ThreadPoolExecutor,而不是使用Executors.xxx来创建,后者,没有提供一些参数配置。然后配置是比较重要的,对于使用线程池也比较有用,否则容易出现一些不容易理解的错误。

创建的demo:

ExecutorService executorService = new ThreadPoolExecutor(2,
                2,
                60L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2),
                new ThreadPoolExecutor.DiscardPolicy());

不管是jdk的ThreadPoolExecutor还是Spring的ThreadPoolTaskExecutor,对于创建线程的过程都是一样的:

当往线程池中提交新任务时,线程池主要流程如下:
核心线程数 -> 线程队列 -> 最大线程数 -> 拒绝策略
a. 如果池中任务数 < corePoolSize (核心线程数),创建新线程立即执行任务
b. 如果池中任务数 > corePoolSize,新任务放到缓存队列当中等待执行
c. 队列满,线程数量 d. 队列满,线程数量>=maxPoolSize,使用拒绝策略拒绝

1.1. 超过maxPoolSize后直接丢弃策略
@Test
    public void testJdkExecutorServiceRunnable() throws InterruptedException {

        ExecutorService executorService = new ThreadPoolExecutor(2,
                2,
                60L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2),
                new ThreadPoolExecutor.DiscardPolicy());

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info("hello" + finalI);
                }
            });
        }

        Thread.sleep(5000);
    }

结果说明:

普通的Runnable线程
1 jdk线程池,ExecutorService接口
2. new ThreadPoolExecutor方式创建
3. ThreadPoolExecutor.DiscardPolicy超过 (核心线程数 + 队列数)之后,会被默默的丢弃,不会抛异常,也不会打印日志。
运行结果:
15:35:06.880 [pool-1-thread-2] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - hello1
15:35:06.880 [pool-1-thread-1] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - hello0
15:35:08.888 [pool-1-thread-2] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - hello2
15:35:08.888 [pool-1-thread-1] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - hello3
1.2. 提交Callable对象,可以获取结果 1.2.1. 非并行获取结果
@Test
    public void testJdkExecutorServiceCallable() throws InterruptedException {

        ExecutorService executorService = new ThreadPoolExecutor(10,
                20,
                60L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2),
                new ThreadPoolExecutor.DiscardPolicy());

        List> futureList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Future future = executorService.submit(new MyCallable().setName(String.valueOf(i)));
            futureList.add(future);
        }
        // 获取结果(注意这里没有使用并行流,所以,如果在获取某个结果的时候等待时间较长,可能其他线程的获取结果瞬间就会拿到,看看执行结果就会明白)
        futureList.stream().forEach(e -> {
            try {
                log.info("执行结果[{}]:", new Object[]{e.get()});
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } catch (ExecutionException ex) {
                ex.printStackTrace();
            }
        });

        Thread.sleep(100 * 1000);
    }

执行结果1:

运行结果1:
16:16:19.662 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[0]睡了:9秒]:
16:16:19.668 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[1]睡了:0秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[2]睡了:3秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[3]睡了:1秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[4]睡了:4秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[5]睡了:1秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[6]睡了:4秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[7]睡了:3秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[8]睡了:8秒]:
16:16:19.669 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[9]睡了:7秒]:
分析:
由于获取第1个线程结果的时候,第1个线程睡了9s,导致后面都已经有结果不用等待,所以瞬间在16:16:19就可以拿到所有的结果。

执行结果2:

运行结果2:
16:17:04.278 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[0]睡了:4秒]:
16:17:04.283 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[1]睡了:0秒]:
16:17:04.283 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[2]睡了:0秒]:
16:17:04.283 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[3]睡了:1秒]:
16:17:08.278 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[4]睡了:8秒]:
16:17:08.279 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[5]睡了:6秒]:
16:17:08.279 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[6]睡了:6秒]:
16:17:08.279 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[7]睡了:8秒]:
16:17:09.274 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[8]睡了:9秒]:
16:17:09.274 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[9]睡了:9秒]:
分析:
第1个线程睡了4s,所以导致前4个线程是一起拿到结果的。
第5个线程睡了8s,所以5-8个线程是一起拿到结果的
最后2个线程,都是睡了9s,所以是一起拿到结果。
1.2.2. 并行获取结果
@Test
    public void testJdkExecutorServiceCallableStreamParallel() throws InterruptedException {

        ExecutorService executorService = new ThreadPoolExecutor(10,
                20,
                60L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2),
                new ThreadPoolExecutor.DiscardPolicy());

        List> futureList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Future future = executorService.submit(new MyCallable().setName(String.valueOf(i)));
            futureList.add(future);
        }
        // 获取结果(与上一个案例不同的,就是使用了parallel()方法,并行获取结果。)
        futureList.stream().parallel().forEach(e -> {
            try {
                log.info("执行结果[{}]:", new Object[]{e.get()});
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } catch (ExecutionException ex) {
                ex.printStackTrace();
            }
        });

        Thread.sleep(100 * 1000);
    }

执行结果1:

执行结果1比较理想,都是睡的短的先拿到结果:
17:29:14.887 [ForkJoinPool.commonPool-worker-1] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[2]睡了:3秒]:
17:29:14.887 [ForkJoinPool.commonPool-worker-6] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[0]睡了:3秒]:
17:29:15.883 [ForkJoinPool.commonPool-worker-6] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[5]睡了:4秒]:
17:29:15.883 [ForkJoinPool.commonPool-worker-7] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[9]睡了:4秒]:
17:29:17.883 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[6]睡了:6秒]:
17:29:18.880 [ForkJoinPool.commonPool-worker-3] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[1]睡了:7秒]:
17:29:19.883 [ForkJoinPool.commonPool-worker-4] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[7]睡了:8秒]:
17:29:19.883 [ForkJoinPool.commonPool-worker-2] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[8]睡了:8秒]:
17:29:20.882 [ForkJoinPool.commonPool-worker-5] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[4]睡了:9秒]:
17:29:20.882 [ForkJoinPool.commonPool-worker-1] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[3]睡了:9秒]:

执行结果2:

运行结果2(睡了0s的,等了3s后才输出,这个应该是跟stream的parallel概念相关,后面专题看stream的时候再整理):
17:32:21.578 [ForkJoinPool.commonPool-worker-4] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[7]睡了:3秒]:
17:32:21.584 [ForkJoinPool.commonPool-worker-4] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[5]睡了:0秒]:
17:32:21.584 [ForkJoinPool.commonPool-worker-4] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[3]睡了:1秒]:
17:32:23.571 [ForkJoinPool.commonPool-worker-7] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[4]睡了:5秒]:
17:32:23.571 [ForkJoinPool.commonPool-worker-6] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[0]睡了:5秒]:
17:32:23.571 [ForkJoinPool.commonPool-worker-1] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[2]睡了:5秒]:
17:32:24.575 [ForkJoinPool.commonPool-worker-3] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[1]睡了:6秒]:
17:32:25.574 [ForkJoinPool.commonPool-worker-5] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[9]睡了:7秒]:
17:32:26.570 [main] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[6]睡了:8秒]:
17:32:27.572 [ForkJoinPool.commonPool-worker-2] INFO com.example.simple.jdk_concurrent.TestJdkConcurrent - 执行结果[Callable[8]睡了:9秒]:
结论是:不一定使用了parallel()就一定会按照睡的时间由短到长得到结果。
2. Spring线程池 2.1. 概览

下图是spring的scheduling包关于ThreadPoolTaskExecutor的概览。划线的,就是我们要关注的重点。

直接上代码,边写边看边理解。

2.2. 配置线程池
package com.example.simple.spring_scheduling;

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

import lombok.Data;

@Configuration
public class ThreadPoolConfig {

    ThreadPoolProperties properties = new ThreadPoolProperties();

    
    @Bean(name = "taskExecutor-for-pay")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(properties.getCorePoolSize());
        executor.setMaxPoolSize(properties.getMaxPoolSize());
        executor.setQueueCapacity(properties.getQueueCapacity());
        executor.setThreadNamePrefix(properties.getThreadNamePrefix());
        // 设置线程保持活跃的时间(默认:60)
        executor.setKeepAliveSeconds(properties.getKeepAliveTime());
        // 当任务完成后,长时间无待处理任务时,销毁线程池
        executor.setWaitForTasksToCompleteOnShutdown(properties.isWaitForTasksToCompleteOnShutdown());
        executor.setAwaitTerminationSeconds(properties.getAwaitTerminationSeconds());
        // 设置任务拒绝策略
        
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    @Data
    class ThreadPoolProperties {

        
        private int corePoolSize = 2;
        
        private int maxPoolSize = 4;
        
        private int keepAliveTime;
        
        private int queueCapacity = 3;
        
        private String threadNamePrefix = "tName";

        
        private boolean allowCoreThreadTimeout = false;

        
        private boolean waitForTasksToCompleteOnShutdown = false;

        private int awaitTerminationSeconds;

    }
}

跟jdk的线程池机制一样:

当往线程池中提交新任务时,线程池主要流程如下:
核心线程数 -> 线程队列 -> 最大线程数 -> 拒绝策略
a. 如果池中任务数 < corePoolSize (核心线程数),创建新线程立即执行任务
b. 如果池中任务数 > corePoolSize,新任务放到缓存队列当中等待执行
c. 队列满,线程数量 d. 队列满,线程数量>=maxPoolSize,使用拒绝策略拒绝

2.3. 准备使用线程池的service
package com.example.simple.spring_scheduling;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;


@Service
@EnableAsync
@Slf4j
public class ThreadPoolService {

    @Async("taskExecutor-for-pay")
    public void process(int i) throws InterruptedException {
        log.info(Thread.currentThread().getName() + "开始处理第【" + i + "】个请求");
        Thread.sleep(2 * 1000);
        log.info("第【{}】个请求处理结束", i);
    }

    public void F2() throws InterruptedException {
        for (int i = 0; i < 15; i++) {
            // 想要调起15个线程,可以吗?
            process(i);
        }
    }
}
2.4. 准备controller
package com.example.simple.controller;

import com.example.simple.jdk_concurrent.MyCallable;
import com.example.simple.spring_scheduling.ThreadPoolService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;


@RestController
@Slf4j
public class SampleController {

    @Resource
    ThreadPoolService threadPoolService;

    @Resource(name = "taskExecutor-for-pay")
    ThreadPoolTaskExecutor threadPoolTaskExecutor;

    
    @RequestMapping("/spring/threads/{function}")
    public String testSpringThreadPoolTaskExecutor1(@PathVariable("function") String fun) throws InterruptedException, ExecutionException {
        if (fun.equals("f2")) {
            // 在threadPoolService.F2()方法内,循环调用10次process()方法,process()方法有异步注解,F2方法签名没有异步注解
            threadPoolService.F2();
        }
        else if (fun.equals("process")) {
            // process()方法签名有异步注解,循环调起15次。
            log.info("15个process准备调用。");
            for (int i = 0; i < 15; i++) {
                log.info("准备提交第【{}】个请求", i);
                threadPoolService.process(i);
            }
            log.info("15个process已经发到线程池了。主请求结束。");
        }
        else if (fun.equals("callable")) {
            // 向 threadPoolTaskExecutor 提交Callable任务。
            Future future = threadPoolTaskExecutor.submit(new MyCallable().setName("SpringCallable"));
            // 由于执行了 future.get(), 所以请求会等到get到结果之后才能收到响应。
            log.info("线程执行的结果:" + future.get());
        }
        log.info("开始写响应给请求方。");
        return "hello world";
    }

}

2.5. 调用结果分析
http://localhost:8080/spring/threads/f2
先说结论:
public void F2() throws InterruptedException {
    for (int i = 0; i < 15; i++) {
        // 想要调起15个线程,可以吗?
        process(i);
    }
}
这段代码,看起来是想调起15个process,而process,是用异步注解的,但是实际并没有使用到线程池,打印的日志里,使用的线程都是请求线程。

调用结果:
2022-06-11 19:58:40.274  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【0】个请求
2022-06-11 19:58:42.276  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【0】个请求处理结束
2022-06-11 19:58:42.279  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【1】个请求
2022-06-11 19:58:44.283  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【1】个请求处理结束
2022-06-11 19:58:44.283  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【2】个请求
2022-06-11 19:58:46.287  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【2】个请求处理结束
2022-06-11 19:58:46.287  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【3】个请求
2022-06-11 19:58:48.288  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【3】个请求处理结束
2022-06-11 19:58:48.288  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【4】个请求
2022-06-11 19:58:50.293  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【4】个请求处理结束
2022-06-11 19:58:50.293  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【5】个请求
2022-06-11 19:58:52.298  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【5】个请求处理结束
2022-06-11 19:58:52.299  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【6】个请求
2022-06-11 19:58:54.303  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【6】个请求处理结束
2022-06-11 19:58:54.304  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【7】个请求
2022-06-11 19:58:56.308  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【7】个请求处理结束
2022-06-11 19:58:56.308  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【8】个请求
2022-06-11 19:58:58.309  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【8】个请求处理结束
2022-06-11 19:58:58.309  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【9】个请求
2022-06-11 19:59:00.312  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【9】个请求处理结束
2022-06-11 19:59:00.312  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【10】个请求
2022-06-11 19:59:02.313  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【10】个请求处理结束
2022-06-11 19:59:02.314  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【11】个请求
2022-06-11 19:59:04.314  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【11】个请求处理结束
2022-06-11 19:59:04.315  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【12】个请求
2022-06-11 19:59:06.318  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【12】个请求处理结束
2022-06-11 19:59:06.318  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【13】个请求
2022-06-11 19:59:08.322  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【13】个请求处理结束
2022-06-11 19:59:08.323  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【14】个请求
2022-06-11 19:59:10.323  INFO 11561 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【14】个请求处理结束
2022-06-11 19:59:10.324  INFO 11561 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 开始写响应给请求方。

在继续看,在controller中循环调用15次process,能使用到线程池吗?

else if (fun.equals("process")) {
    // process()方法签名有异步注解,循环调起15次。
    log.info("15个process准备调用。");
    for (int i = 0; i < 15; i++) {
        log.info("准备提交第【{}】个请求", i);
        threadPoolService.process(i);
    }
    log.info("15个process已经发到线程池了。主请求结束。");
}

结果:是可以的,并发可以有效减少任务的执行时间。

http://localhost:8080/spring/threads/process
调用结果:
2022-06-11 20:01:51.445  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 15个process准备调用。
2022-06-11 20:01:51.445  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【0】个请求
2022-06-11 20:01:51.449  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【1】个请求
2022-06-11 20:01:51.450  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【2】个请求
2022-06-11 20:01:51.450  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【3】个请求
2022-06-11 20:01:51.450  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【4】个请求
2022-06-11 20:01:51.450  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【5】个请求
2022-06-11 20:01:51.450  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【6】个请求
2022-06-11 20:01:51.451  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【7】个请求
2022-06-11 20:01:51.455  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : tName1开始处理第【0】个请求
2022-06-11 20:01:51.455  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : tName4开始处理第【6】个请求
2022-06-11 20:01:51.455  INFO 11619 --- [         tName2] c.e.s.s.ThreadPoolService                : tName2开始处理第【1】个请求
2022-06-11 20:01:51.455  INFO 11619 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【7】个请求
2022-06-11 20:01:51.455  INFO 11619 --- [         tName3] c.e.s.s.ThreadPoolService                : tName3开始处理第【5】个请求
2022-06-11 20:01:53.459  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : 第【0】个请求处理结束
2022-06-11 20:01:53.459  INFO 11619 --- [         tName2] c.e.s.s.ThreadPoolService                : 第【1】个请求处理结束
2022-06-11 20:01:53.460  INFO 11619 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【7】个请求处理结束
2022-06-11 20:01:53.459  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : 第【6】个请求处理结束
2022-06-11 20:01:53.460  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【8】个请求
2022-06-11 20:01:53.460  INFO 11619 --- [         tName2] c.e.s.s.ThreadPoolService                : tName2开始处理第【3】个请求
2022-06-11 20:01:53.460  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : tName1开始处理第【2】个请求
2022-06-11 20:01:53.460  INFO 11619 --- [         tName3] c.e.s.s.ThreadPoolService                : 第【5】个请求处理结束
2022-06-11 20:01:53.460  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : tName4开始处理第【4】个请求
2022-06-11 20:01:53.460  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【9】个请求
2022-06-11 20:01:53.461  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【10】个请求
2022-06-11 20:01:53.462  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【11】个请求
2022-06-11 20:01:53.462  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【12】个请求
2022-06-11 20:01:53.462  INFO 11619 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : http-nio-8080-exec-1开始处理第【12】个请求
2022-06-11 20:01:53.462  INFO 11619 --- [         tName5] c.e.s.s.ThreadPoolService                : tName5开始处理第【11】个请求
2022-06-11 20:01:55.462  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : 第【2】个请求处理结束
2022-06-11 20:01:55.462  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : 第【4】个请求处理结束
2022-06-11 20:01:55.462  INFO 11619 --- [         tName2] c.e.s.s.ThreadPoolService                : 第【3】个请求处理结束
2022-06-11 20:01:55.463  INFO 11619 --- [         tName5] c.e.s.s.ThreadPoolService                : 第【11】个请求处理结束
2022-06-11 20:01:55.463  INFO 11619 --- [         tName2] c.e.s.s.ThreadPoolService                : tName2开始处理第【10】个请求
2022-06-11 20:01:55.463  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : tName4开始处理第【9】个请求
2022-06-11 20:01:55.463  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : tName1开始处理第【8】个请求
2022-06-11 20:01:55.463  INFO 11619 --- [nio-8080-exec-1] c.e.s.s.ThreadPoolService                : 第【12】个请求处理结束
2022-06-11 20:01:55.464  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【13】个请求
2022-06-11 20:01:55.464  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 准备提交第【14】个请求
2022-06-11 20:01:55.464  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 15个process已经发到线程池了。主请求结束。
2022-06-11 20:01:55.464  INFO 11619 --- [nio-8080-exec-1] c.e.simple.controller.SampleController   : 开始写响应给请求方。
2022-06-11 20:01:57.464  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : 第【9】个请求处理结束
2022-06-11 20:01:57.464  INFO 11619 --- [         tName2] c.e.s.s.ThreadPoolService                : 第【10】个请求处理结束
2022-06-11 20:01:57.464  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : 第【8】个请求处理结束
2022-06-11 20:01:57.464  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : tName4开始处理第【13】个请求
2022-06-11 20:01:57.464  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : tName1开始处理第【14】个请求
2022-06-11 20:01:59.465  INFO 11619 --- [         tName4] c.e.s.s.ThreadPoolService                : 第【13】个请求处理结束
2022-06-11 20:01:59.465  INFO 11619 --- [         tName1] c.e.s.s.ThreadPoolService                : 第【14】个请求处理结束

最后就是简单了解一下,是否可以向threadPoolTaskExecutor提交callable任务?就是说在线程执行完成后获取到结果?

else if (fun.equals("callable")) {
    // 向 threadPoolTaskExecutor 提交Callable任务。
    Future future = threadPoolTaskExecutor.submit(new MyCallable().setName("SpringCallable"));
    // 由于执行了 future.get(), 所以请求会等到get到结果之后才能收到响应。
    log.info("线程执行的结果:" + future.get());
}

上面threadPoolTaskExecutor是通过名称注入到controller中的:
@Resource(name = "taskExecutor-for-pay")
ThreadPoolTaskExecutor threadPoolTaskExecutor;

执行结果也简单,就是在拿到结果之后,写响应给调用方。
当然,如果我们没有显示的调用get()方法,那就会直接写响应了。
2022-06-12 23:21:37.152  INFO 19311 --- [nio-8080-exec-4] c.e.simple.controller.SampleController   : 线程执行的结果:Callable[SpringCallable]睡了:1秒
2022-06-12 23:21:37.155  INFO 19311 --- [nio-8080-exec-4] c.e.simple.controller.SampleController   : 开始写响应给请求方。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/969867.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号