线程池详解

本文详细介绍了Java中线程池的四种初始化方式及其优缺点,重点讲解了线程池的七大参数以及工作顺序。同时,探讨了线程池在资源管理和响应速度方面的优势。此外,文章深入探讨了CompletableFuture的异步编排,包括创建异步对象、计算完成时回调方法、线程串行化、多任务组合等场景,并举例说明如何在实际开发中提高并发性能和响应效率。
摘要由CSDN通过智能技术生成

一、初始化线程池的4种方式

1、继承 Thread

2、实现 Runnable

3、实现 Callable 接口 + FutureTask(可以拿到返回结果,可以处理异常)

4、线程池
方式一和方式二,主进程无法获取线程的运行结果,不适用当前的场景
方式三:主进程可以获取当前先后曾的运行结果,但是不利于控制服务器中的线程资源。
方式四:通过如下两种方式初始化线程

Executors.newFixedThreadPool(3);
//或者
new ThreadPollExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit,unit,workQueue,threadFactory,handler);
//七个参数

通过线程池性能稳定,也可以获取执行结果,并捕获异常,但是,在业务复杂情况下,一个异步调用可能会依赖另一个异步调用的执行结果

二、线程池的 7 大参数

  • corePoolSize: 保留在池中的线程数 即使处于空闲状态 除非设置了allowCoreThreadTimeOut
  • maximumPoolSize: 池中允许的最大线程数
  • keepalivueTime: 存活时间 如果当前线程大于core的数量释放空闲的线程 maximumPoolsize-corePoolSize 只要线程空闲大于指定的keepAlivue
  • TimeUnit:时间单位
  • BlockingQUeue< Runnable > workQueue: 阻塞队列 如果任务有很多 就会将目前多的任务放在队列里面
    只要有线程空闲,就会去队列里面取出新的任务继续执行
  • threadFactory: 线程创建工厂
  • RejectedExecutionHandler: 如果队列满了 按照我们指定得拒绝策略拒绝指定任务

三、工作顺序

1)、线程池创建好 准备好core数量的核心线程,准备接受任务
1.1、core满了 就将在进来的任务放入阻塞队列中 空闲的core就会自己去阻塞队列获取任务执行
1.2、阻塞队列满了 就直接开新线程执行 最大只能开到max指定数量
1.3、max满了就用RejectedExecutionHandler 拒绝任务
1.4、max都执行完成,有很多空闲 指定时间以后keepAlivueTime以后 释放max-core(195)这些线程
new LinkedBlockingQueue<>() 默认是Integer最大值 内存不够
一个线程池 core:7 max:20 queue:50 100并发进来怎么分配
面试题:
7个会立即得到执行 50个进入队列 再开13个进行执行,剩下的30个就使用拒绝策略
如果不想抛弃还要执行 CallerRunsPolicy 同步方式

四、常见的 4 种线程池

  • newCacheThreadPool
    • 创建一个可缓存的线程池,如果线程池长度超过需要,可灵活回收空闲线程,若无可回收,则新建线程
  • newFixedThreadPool
    • 创建一个指定长度的线程池,可控制线程最大并发数,超出的线程会再队列中等待
  • newScheduleThreadPool
    • 创建一个定长线程池,支持定时及周期性任务执行
  • newSingleThreadExecutor
    • 创建一个单线程化的线程池,她只会用唯一的工作线程来执行任务,保证所有任务

下面是创建线程池的两种方式:原始创建方式和快速创建方式。

//最原始的方式:
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
                200,
                10, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());


        //快速创建线程池
        Executors.newCachedThreadPool(); //core是0 所有都可回收
        Executors.newFixedThreadPool(10);//固定大小 core=max 都不可以回收
        Executors.newScheduledThreadPool(10); //定时任务的线程池
        Executors.newSingleThreadExecutor(); //单线程的线程池,后台从队列里面获取任务 挨个执行

五、开发中为什么使用线程池

  • 降低资源的消耗
    通过重复利用已创建好的线程降低线程的创建和销毁带来的损耗
  • 提高响应速度
    因为线程池中的线程没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
  • 提高线程的客观理性
    线程池会根据当前系统的特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销,无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

六、CompletableFuture 异步编排

业务场景:

查询商品详情页逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间
假如商品详情页的每个查询,需要如下标注时间才能完成

那么,用户需要5.5s后才能看到商品相详情页的内容,很显然是不能接受的

如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应

1、创建异步对象

CompletableFuture 提供了四个静态方法来创建一个异步操作
1、runXxx 都是没有返回结果的,supplyXxxx都是可以获取返回结果的

2、可以传入自定义的线程池,否则就是用默认的线程池

3、根据方法的返回类型来判断是否该方法是否有返回类型
代码实现:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main....start.....");
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }, executor);

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, executor);
        //阻塞等待值的返回
        Integer integer = future.get();

        System.out.println("main....stop....." + integer);
    }

2、计算完成时回调方法

whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况

whenComplete 和 whenCompleteAsync 的区别

​ whenComplete :是执行当前任务的线程继续执行 whencomplete 的任务

​ whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getId());
    int i = 10 / 0;
    System.out.println("运行结果:" + i);
    return i;
}, executor).whenComplete((res,exception) ->{
    // 虽然能得到异常信息,但是没法修改返回的数据
    System.out.println("异步任务成功完成了...结果是:" +res + "异常是:" + exception);
}).exceptionally(throwable -> {
    // 可以感知到异常,同时返回默认值
    return 10;
});

3、handle 方法

和 complete 一样,可以对结果做最后的处理(可处理异常),可改变返回值

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getId());
    int i = 10 / 2;
    System.out.println("运行结果:" + i);
    return i;
}, executor).handle((res,thr) ->{
    if (res != null ) {
        return res * 2;
    }
    if (thr != null) {
        return 0;
    }
    return 0;
});

4、线程串行方法

thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值

thenAccept方法:消费处理结果,接受任务处理结果,并消费处理,无返回结果

thenRun 方法:只要上面任务执行完成,就开始执行 thenRun ,只是处理完任务后,执行 thenRun的后续操作

带有 Async 默认是异步执行的,同之前,

以上都要前置任务完成
在这里插入图片描述

        /**
         * 线程串行化,
         * 1、thenRun:不能获取到上一步的执行结果,无返回值
         * .thenRunAsync(() ->{
         *             System.out.println("任务2启动了....");
         *         },executor);
         * 2、能接受上一步结果,但是无返回值 thenAcceptAsync
         * 3、thenApplyAsync 能收受上一步结果,有返回值
         *
         */
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, executor).thenApplyAsync(res -> {
            System.out.println("任务2启动了..." + res);
            return "Hello " + res;
        }, executor);
        String s = future.get();

        System.out.println("main....stop....." + s);

5、两任务组合 - 都要完成

两个任务必须都完成,触发该任务

thenCombine: 组合两个 future,获取两个 future的返回结果,并返回当前任务的返回值

thenAccpetBoth: 组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值

runAfterBoth:组合 两个 future,不需要获取 future 的结果,只需要两个 future处理完成任务后,处理该任务。

 /**
         * 两个都完成
         */
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1当前线程:" + Thread.currentThread().getId());
            int i = 10 / 4;
            System.out.println("任务1结束:" + i);
            return i;
        }, executor);

        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2当前线程:" + Thread.currentThread().getId());
            System.out.println("任务2结束:");
            return "Hello";
        }, executor);

        // f1 和 f2 执行完成后在执行这个
//        future01.runAfterBothAsync(future02,() -> {
//            System.out.println("任务3开始");
//        },executor);

        // 接收f1 和 f2 的运行结果传参进去,没有值返回
//        future01.thenAcceptBothAsync(future02,(f1,f2) -> {
//            System.out.println("任务3开始....之前的结果:" + f1 + "==>" + f2);
//        },executor);

         // 接收f1 和 f2 的运行结果传参进去,有值返回
        CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
            return f1 + ":" + f2 + "-> Haha";
        }, executor);

        System.out.println("main....end....." + future.get());

6、两任务组合,一个完成即可

当两个任务中,任意一个future 任务完成时,执行任务
applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值

acceptEither: 两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值

runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值

/**
         * 两个任务,只要有一个完成,我们就执行任务
         * runAfterEnitherAsync:不感知结果,自己没有返回值
         * acceptEitherAsync:感知结果,自己没有返回值
         *  applyToEitherAsync:感知结果,自己有返回值
         */
//        future01.runAfterEitherAsync(future02,() ->{
//            System.out.println("任务3开始...之前的结果:");
//        },executor);

//        future01.acceptEitherAsync(future02,(res) -> {
//            System.out.println("任务3开始...之前的结果:" + res);
//        },executor);

        CompletableFuture<String> future = future01.applyToEitherAsync(future02, res -> {
            System.out.println("任务3开始...之前的结果:" + res);
            return res.toString() + "->哈哈";
        }, executor);

7、多任务组合

allOf:等待所有任务完成

anyOf:只要有一个任务完成

   CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "hello.jpg";
        });

        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性");
            return "黑色+256G";
        });

        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("查询商品介绍");
            return "华为";
        });

        // 等待全部执行完
//        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
//        allOf.get();

        // 只需要有一个执行完
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
        anyOf.get();
        System.out.println("main....end....." + anyOf.get());

都是操作 CompletableFuture 类 更多方法还请参考该类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

量化接口stockapi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值