C C++最全Java多线程(六):线程池详解_executors,2024年最新你头秃都没想到还能这样吧

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

运行结果

f5f73caaa109477fbb5186130114c8ac.png

3.2 带缓存的线程池(Executors.newCachedThreadPool)

线程池会根据任务数创建线程,并且在一定时间内可以重复使用这些线程。

示例代码

public class ThreadPoolDemo4 {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            service.submit(() -> System.out.println("i:" + finalI + " 线程名称:" + Thread.currentThread().getName()));
        }
    }
}

运行结果

46ec94f8e0684a9fb0e4dc50f984b317.png

该方式适用于短时间有且有大量任务的场景,它的缺点是可能占用很多资源。

3.3 执行定时任务(Executors.newSingleThreadExecutor)

3.3.1 延迟执行(1次)

示例代码

public class ThreadPoolDemo12 {
    public static void main(String[] args) {
        ScheduledExecutorService threadPool =
                Executors.newScheduledThreadPool(10);
        // 定时任务
        System.out.println("设置定时任务:" + new Date());
        // 延迟 n 秒后执⾏(只执⾏⼀次)
        threadPool.schedule(() -> System.out.println("schedule:" + new Date()), 2, TimeUnit.SECONDS);
    }
}

执行结果

24d99ba8b075450c9b6fbf7c3de1208f.png

延迟 2s 后执行一次。

3.3.2 固定频率执行( scheduleAtFixedRate)

示例代码

public class ThreadPoolDemo13 {
    public static void main(String[] args) {
        ScheduledExecutorService threadPool =
                Executors.newScheduledThreadPool(10);
        // 定时任务
        System.out.println("设置定时任务:" + new Date());
        threadPool.scheduleAtFixedRate(() -> 
                System.out.println("scheduleAtFixedRate:" + new Date()), 3, 2, TimeUnit.SECONDS);
    }
}

运行结果

d02c62b4d17244a99833d62de95ad1c3.png

延迟3s后执行,之后每2s执行一次。

参数解释

  1. 参数1:执行任务;
  2. 参数2:延迟 n 秒后执行;
  3. 参数3:执行定时任务的频率;
  4. 参数4:配合参数3使用的时间单位。
3.3.3 scheduleAtFixedRate VS scheduleWithFixedDelay

scheduleAtFixedRate 是以上⼀次任务的开始时间,作为下次定时任务的参考时间的(参考时间+延迟任务=任务执行)。

示例代码

public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        // 创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务执行时间:" + LocalDateTime.now());

        // 2s之后开始执行定时任务,定时任务每隔4s执行一次
        service.scheduleAtFixedRate(() -> {
            System.out.println("执行了任务:" + LocalDateTime.now());
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 2, 4, TimeUnit.SECONDS);
    }
}

运行结果

d2eaa030a21045d8abb210f898cc803c.png

2s后开始执行定时任务,每隔5s执行一次。

设置的是每隔4秒执行一次定时任务,为什么实际上是5s执行一次呢?

6c048b2483f6b0a991f2625df7ba443a.jpg

注意,如果执行任务时间大于设置的定时任务执行时间,那么此方法会以执行任务的时间为准,简而言之,就是哪个时间长就以哪个时间作为定时任务执行的周期。

scheduleWithFixedDelay 是以上⼀次任务的结束时间,作为下次定时任务的参考时间的。

示例代码

public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        // 创建线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务执行时间:" + LocalDateTime.now());

        // 2s之后开始执行定时任务,每次执行间隔4秒
        service.scheduleWithFixedDelay(() -> {
            System.out.println("执行了任务:" + LocalDateTime.now());
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 2, 4, TimeUnit.SECONDS);
    }
}

运行结果

31ee960d3f3f401a9898399ad76aa684.png

2s后开始执行任务,每隔9秒执行一次定时任务。

为什么这个也不是每隔4s执行一次,而是9s呢???

a9682dd3d74f6e825d0520c181230ffe.jpg

因为  scheduleWithFixedDelay 是以上⼀次任务的结束时间,作为下次定时任务的参考时间的,上个任务执行5s后,再延时4s执行延时任务。

3.4 定时任务单线程(Executors.newSingleThreadScheduledExecutor)

示例代码

public class ThreadPoolDemo6 {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        System.out.println("添加任务时间:" + LocalDateTime.now());
        service.schedule(() -> System.out.println("执行任务:" + LocalDateTime.now()), 2, TimeUnit.SECONDS);
    }
}

运行结果

acd4cb63045e48f9a81563e624f37dba.png

3.5 单线程线程池(Executors.newSingleThreadExecutor)

示例代码

public class ThreadPoolDemo7 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            service.submit(() -> System.out.println("任务:" + finalI + ", 线程名:" + Thread.currentThread().getName()));
        }
    }
}

运行结果

23cf3518d934489eba24f34b44b655a5.png

单线程的线程池有什么意义呢?

  1. 自定义拒绝策略;
  2. 提供了任务队列和任务管理的功能。

3.6 根据当前CPU生成线程池(Executors.newWorkStealingPool)

示例代码

public class ThreadPoolDemo8 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newWorkStealingPool();
        for (int i = 0; i < 100; i++) {
            service.submit(() -> System.out.println("线程名:" + Thread.currentThread().getName()));
        }
        while (!service.isTerminated()){
        }
    }
}

运行结果

3282d33482c2420a841adc8ce2913961.png

3.7 手动方式(ThreadPoolExecutor)

3.7.1 创建忽略最新任务的线程池

示例代码

public class ThreadPoolDemo11 {
    public static void main(String[] args) {
        ThreadFactory factory = r -> {
            Thread thread = new Thread(r);
            return thread;
        };

        // 手动方式创建线程池
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(2, 2, 10, TimeUnit.SECONDS,
                        new LinkedBlockingDeque<>(2), factory, new ThreadPoolExecutor.DiscardPolicy());
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(finalI * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行任务" + finalI);
            });
        }
        // 终止线程池
        executor.shutdown();
    }
}

运行结果

5e02e5d8fdb9479e8540bef1dfbaf048.png

3.7.2 ThreadPoolExecutor 参数说明
  1. corePoolSize:核心线程数,可以大致理解为长期驻留的线程数目(除非设置了allowCoreThreadTimeOut)。对于不同的线程池,这个值可能会有很大区别,比如newFixedThreadPool 会将其设置为 nThreads,而对于 newCachedThreadPool 则是为 0。
  2. maximumPoolSize:顾名思义,就是线程不够时能够创建的最⼤线程数。同样进⾏对比,对于newFixedThreadPool,当然就是 nThreads,因为其要求是固定大小,而 newCachedThreadPool 则是 Integer.MAX_VALUE。
  3. keepAliveTime:空闲线程的保活时间,如果线程的空闲时间超过这个值,那么将会被关闭。注意此值生效条件必须满足:空闲时间超过这个值,并且线程池中的线程数少于等于核⼼线程数corePoolSize。当然核心线程默认是不会关闭的,除非设置了allowCoreThreadTimeOut(true)那么核心线程也可以被回收。
  4. TimeUnit:时间单位。
  5. BlockingQueue:任务队列,用于存储线程池的待执行任务的。
  6. threadFactory:⽤于生成线程,⼀般我们可以⽤默认的就可以了。
  7. handler:当线程池已经满了,但是又有新的任务提交的时候,该采取什么策略由这个来指定。有几种方式可供选择,像抛出异常、直接拒绝然后返回等,也可以自己实现相应的接口实现自己的逻辑。
3.7.3 线程池执行流程

113ca706d90145e7a0c447a03452a610.png

3.7.4 拒绝策略(5种(4(JDK提供的) + 1(自定义拒绝策略)))

JDK提供的四种拒绝策略:

5376df4b44604ccea59dc230a36a04a2.png

  1. DiscardPolicy : 忽略旧任务(队列第一个任务)
  2. AbortPolicy : 提示异常,拒绝执行(默认的拒绝策略)
  3. CallerRunsPolicy : 使用调用线程池的线程来执行任务
  4. DiscardOldestPolicy :  忽略最新任务

自定义拒绝策略

示例代码

public class ThreadPoolDemo10 {
    public static void main(String[] args) {
        ThreadFactory factory = r -> {
            Thread thread = new Thread(r);
            return thread;
        };

        // 手动方式创建线程池
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(2, 2, 10, TimeUnit.SECONDS,
                        new LinkedBlockingDeque<>(2), factory,
                        new RejectedExecutionHandler() {
                            @Override
                            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                                // 自定义拒绝策略
                                System.out.println("自定义拒绝策略");
                            }
                        });
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(finalI * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行任务" + finalI);
            });
        }
        // 终止线程池
        executor.shutdown();
    }
}

运行结果:

595f02605134493bb8277ddb1d3642a2.png

4. 线程池状态

93068991d79144209ed23ac26517d1de.png

查看 ThreadPoolExecutor 源码可知线程的状态如下:

5f98cbbaf56148ad99837e1137b21abe.png

  • RUNNING:这是最正常的状态:接受新的任务,处理等待队列中的任务;
  • SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务;
  • STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程;
  • TIDYING:所有的任务都销毁了,workCount 为 0。线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated();
  • TERMINATED:terminated() 方法结束后,线程池的状态就会变成这个。

各个状态的转换过程有以下几种:

  • RUNNING -> SHUTDOWN:当调用了 shutdown() 后,会发生这个状态转换,这也是最重要的
  • (RUNNING or SHUTDOWN) -> STOP:当调⽤ shutdownNow() 后,会发⽣这个状态转换;
  • SHUTDOWN -> TIDYING:当任务队列和线程池都清空后,会由 SHUTDOWN 转换为 TIDYING;
  • STOP -> TIDYING:当任务队列清空后,发⽣这个转换;
  • TIDYING -> TERMINATED:这个前面说了,当 terminated() 方法结束后。

shutdown VS shutdownNow :

  • shutdown 执行时线程池终止接收新任务,并且会将任务队列中的任务处理完;
  • shutdownNow 执行时线程池终止接收新任务,并且会给终止执行任务队列中的任务。

shutdown :

public class ThreadPoolDemo10 {
    public static void main(String[] args) {
        ThreadFactory factory = r -> {
            Thread thread = new Thread(r);
            return thread;


![img](https://img-blog.csdnimg.cn/img_convert/093bf4e459586d303c0485e75bad0af6.png)
![img](https://img-blog.csdnimg.cn/img_convert/18cea2bdc80c45b0a8b5f6ba8f72a7f1.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

hutdown :**



public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory = r -> {
Thread thread = new Thread®;
return thread;

[外链图片转存中…(img-Ro3CZWqa-1715693536359)]
[外链图片转存中…(img-KVSVtobd-1715693536359)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值