实习日志-线程池再研究

声明:涉及到隐私问题,故该系列文章不会有关项目详细的代码实现与项目细节,仅用作个人记录。

一. 背景

承接上篇link
现在项目中有以下线程池(starter中部分):

    @Bean(name = "threadPoolExecutor")
    @ConditionalOnMissingBean({ThreadPoolExecutor.class})
    public ThreadPoolExecutor createThreadPoolExecutor() {
        // 创建线程池
        BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("query-pool-%d")
                .daemon(true).build();
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(/*参数*/);
        return threadPoolExecutor;
    }


    @Bean("chainThreadPoolTaskExecutor")
    @ConditionalOnMissingBean({ChainThreadPoolTaskExecutor.class})
    public AsyncTaskExecutor taskExecutor() {
        log.info("taskExecutor()执行");
        ThreadPoolConfigProperties configProperties = properties;

        ThreadPoolTaskExecutor executor = new ChainThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("Script-Executor");
         /*设置其他参数*/
        executor.initialize();
        return executor;
    }

针对上面两个线程池一知半解,通过项目中的代码也没有明确出他们使用的具体的场景。现在结合网上一些资料与代码测试跑一下区别。

二 代码区别

仔细看上面两个线程池,ThreadPoolExecutor是juc包下的,AsyncTaskExecutor与ThreadPoolTaskExecutor是spring的。ThreadPoolExecutor的类间关系如下:

image.png

ThreadPoolExecutor源代码中,都是基于ThreadPoolExecutor做的封装,所以ThreadPoolExecutor是Spring提供的基于ThreadPoolExecutor的封装的线程池。功能大体相同。这里仍然只研究他们的执行任务上的区别。

三 使用区别与方式

ThreadPoolExecutor的用法就不再累赘。
AsyncTaskExecutor主要是配合@Async使用,达到异步的作用。本来多线程就是异步的一种实现方式,这里的异步当然也是多线程来实现。代码示例如下:

@Component
public class TestAsyncMethod {

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource(name = "chainThreadPoolTaskExecutor")
    private AsyncTaskExecutor asyncTaskExecutor;

    public static Random random = new Random();

    @Async("chainThreadPoolTaskExecutor")//指定线程池,可能容器中存在其他异步线程池
    public void taskOne() throws Exception {
        System.out.println("开始执行任务一");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long end = System.currentTimeMillis();
        System.out.println("任务一执行时间:" + (end - start));

        System.out.println("taskOne name = " + Thread.currentThread().getName());
    }
      // 省略 taskTwo() , taskThree()代码 因与taskOne()类似

    public void taskFour() throws Exception {
        System.out.println("开始执行任务四");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));
        long end = System.currentTimeMillis();
        System.out.println("任务四执行时间:" + (end - start));
        System.out.println("taskFour name = " + Thread.currentThread().getName());
    }
   // 省略 taskFive() , taskSix()代码 因与taskThree()类似

    /*************************************************线程中存在返回值*************************************************************************/

    @Async("mdcThreadPoolTaskExecutor")
    public void taskSeven() throws Exception {
        System.out.println("开始执行任务七");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(1000));

        Future future = threadPoolExecutor.submit(() -> {
            return getValue();
        });

        //阻塞当前线程
        System.out.println(future.get());

        Future future1 = threadPoolExecutor.submit(() -> {
            try {
                if ((Integer) future.get() == 1) {
                    System.out.println("future1得到了future的结果");
                }
                System.out.println("future1结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

        });
        long end = System.currentTimeMillis();
        System.out.println("任务七执行时间:" + (end - start));
        System.out.println("taskSeven name = " + Thread.currentThread().getName());
    }

    public int getValue() throws InterruptedException {
        Thread.sleep(6000);
        return 1;
    }
}

测试方法与注释


    @Resource
    private ThreadPoolExecutor threadPoolExecutor;
    @Resource(name = "chainThreadPoolTaskExecutor")
    private AsyncTaskExecutor asyncTaskExecutor;
    /**
     * 普通调用
     * 在主线程中调用方法
     * 4-5-6顺序执行,且均为当前主线程
     * 开始执行任务四
     * 任务四执行时间:670
     * taskFour name = http-nio-8080-exec-1
     * 开始执行任务五
     * 任务五执行时间:785
     * taskFive name = http-nio-8080-exec-1
     * 开始执行任务六
     * 任务六执行时间:590
     * taskSix name = http-nio-8080-exec-1
     * test1 name = http-nio-8080-exec-1
     */
    public String test1() throws Exception {
        testAsyncMethod.taskFour();
        testAsyncMethod.taskFive();
        testAsyncMethod.taskSix();//主线程等待456执行完成继续
        System.out.println("test1 name = " + Thread.currentThread().getName());
        return "over";
    }

    /**
     * 在主线程中从线程池中拿出一个线程 执行方法 主线程继续向下执行
     * 三个任务为顺序 同步执行 使用的线程为同一个
     * test2 name = http-nio-8080-exec-2
     * 开始执行任务四
     * 任务四执行时间:485
     * taskFour name = query-pool-5
     * 开始执行任务五
     * 任务五执行时间:358
     * taskFive name = query-pool-5
     * 开始执行任务六
     * 任务六执行时间:881
     * taskSix name = query-pool-5
     */
    public String test2() throws Exception {
        threadPoolExecutor.execute(()->{
            try {
                testAsyncMethod.taskFour();
                testAsyncMethod.taskFive();
                testAsyncMethod.taskSix();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });//主线程继续向下执行 不等待上述线程
        System.out.println("test2 name = " + Thread.currentThread().getName());
        return "over";
    }

    /**
     * 使三个任务异步执行
     * 实现效果为@Async
     * test3 name = http-nio-8080-exec-7
     * 开始执行任务四
     * 开始执行任务五
     * 开始执行任务六
     * 任务四执行时间:49
     * taskFour name = query-pool-4
     * 任务六执行时间:510
     * taskSix name = query-pool-14
     * 任务五执行时间:912
     * taskFive name = query-pool-1
     */
    public String test3() throws Exception {
        threadPoolExecutor.execute(()->{
            try {
                testAsyncMethod.taskFour();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        threadPoolExecutor.execute(()->{
            try {
                testAsyncMethod.taskFive();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        threadPoolExecutor.execute(()->{
            try {
                testAsyncMethod.taskSix();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });//主线程不等待上述线程
        System.out.println("test3 name = " + Thread.currentThread().getName());
        return "over";
    }

    /**
     * 注解@Async使用
     * 会开三个线程
     * 效果同test3
     */
    public String test4() throws Exception {
        testAsyncMethod.taskOne();
        testAsyncMethod.taskTwo();
        testAsyncMethod.taskThree();
        System.out.println("test4 name = " + Thread.currentThread().getName());
        return "over";
    }

    /********存在返回值调用*********/

    /**
     * test5 name = http-nio-8080-exec-3
     * 开始执行任务七
     * 1
     * 任务七执行时间:6004
     * taskSeven name = Script-Executor4
     * future1得到了future的结果
     * future1结束
     */
    public String testme5() throws Exception {
        testAsyncMethod.taskSeven();//主线程不等待这个线程,继续往下执行
        System.out.println("test5 name = " + Thread.currentThread().getName());
        return "over";
    }


    /**
     * 阻塞时间小于等于12秒
     * @return
     * @throws Exception
     */
    public String testme6() throws Exception {
        long start = System.currentTimeMillis();
        //开启一个线程
        Future future1=asyncTaskExecutor.submit(()->{
            return  testAsyncMethod.getValue();
        });
        //开启一个线程
        Future future2=asyncTaskExecutor.submit(()->{
            return  testAsyncMethod.getValue()+1;
        });
        //未得到值前,主线程阻塞
        System.out.println((Integer) future1.get() + (Integer) future2.get());//3
        long end = System.currentTimeMillis();//理论上时间小于12秒   ---测试时间6000~6001 (ms)
        return end-start +"";
    }
    /**
     * 不难猜想与验证,如果未被@Async注解的方法被asyncTaskExecutor执行,那么效果与threadPoolExecutor一样。毕竟asyncTaskExecutor内部就是threadPoolExecutor。
     */

上述代码主要要注意注释中的线程名。通过上述代码其实就能分辨两个线程池差别了。
值得注意的是,当用asyncTaskExecutor去执行未被@Async注解的方法,作用与threadPoolExecutor是一样的。

大多情况两种线程池可以混用,没有区别。仅对于多个线程任务调度存在差异。

场景区别:如果业务函数task6()依赖task5()的值做计算,那么推荐使用threadPoolExecutor做同步。如果业务函数结果互不影响,如task5()去存数据库表1,task6()去存数据库表2,task7()去计算值,那么使用AsyncExecutor效果更好。若存在线程返回值,仍会阻塞调用future.get()的当前线程,这就即线程中存在返回值且有依赖关系,如上面代码test7。

四 其他

这篇有点灌水文的感觉了,but这个问题分析与解决的过程还是有点僵硬,所以开篇水文记录一下。下一篇重新温习一下 线程,异步,cpu调度...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容