Java 多线程7——线程池各参数的意义 + 四种拒绝策略 + 代码模拟实现


前言

本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下!

本文介绍了线程池的作用,以及线程池的重要参数以及四种拒绝策略,用代码模拟实现了线程池,本节内容理论较多,大家要细细品读。


一、线程池是什么?

线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了 复用线程、便利地管理线程和任务 并将 线程的创建 任务的执行 解耦开来。我们可以创建线程池来 复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗

线程池就是一个🧑🏻‍💻线程管理员,当你有新的任务需要线程去执行时,可以直接去和线程池借一个线程来用( 🔧不再频繁创建线程 ),那么这样创建线程就不是向系统申请,而是从线程池里拿,用完之后再把线程还给线程池( 💉不再随意销毁空闲线程 )。

虽然线程的创建虽然比进程更加轻量,但是频繁创建的情况下,创建线程的这笔开销也是不容忽略的!(程序员不就是让代码跑的又快又准的存在吗!😭😭😭)


二、线程池的使用

1.代码使用线程池

    public static void main(String[] args) {
        // 创建一个固定包含10个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        //像线程池里添加任务
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
    }

2.剖析线程池

ExecutorService threadPool = Executors.newFixedThreadPool(10);
大家一定对这个创建线程池的语句有疑问。

ExecutorService翻译一下,差不多就是执行服务,也就是说这个类是用来执行服务的,用来服务线程池的。

在这里插入图片描述
而这种创建方式,就是 工厂模式 🏭,用来弥补构造方法的不足(参数列表相同,但是要实现的功能不同时,因此这两个功能无法同时存在,因此就用两个方法名不同的静态方法来实现),感兴趣的朋友也可以去深入了解一下🎗️。


🧑🏻‍💻最原装的线程池对象是 ThreadPoolExecutor
上述工厂方法是对这个对象的进一步封装。

🎉俗话说得好,万变不离其宗,所以今天就来讲解一下这个原装的线程池对象。

在这里插入图片描述

上图是原装线程池的四个构造方法,咱们就讲第四个构造方法,它的参数最全,覆盖了上面三个构造方法的所有参数。


1️⃣ 第一个参数:int corePoolSize,核心线程的数量。
核心线程就相当于有编制的正式员工,无论有没有任务,忙不忙,都不会把这个员工辞退,也就是说核心线程不会被销毁,他会伴随线程池的整个生命周期, corePoolSize用来规定核心线程的数量

2️⃣ 第二个参数:int maximumPoolSize,最大线程数。
线程数=核心线程+临时线程 ,当任务比较多,比较忙的时候,线程池就会创建一些临时线程(临时工)来帮忙,当不忙了,闲了,把临时线程销毁(辞退临时工)。 maximumPoolSize用来规定线程池中最多有多少个线程

3️⃣ 第三个参数:long keepAliveTime,临时线程的存活时间。
当不忙之后,核心线程可以忙的过来之后,不会理解销毁临时线程,而是会等待一段时间,如果这段时间又忙起来,就还接着用这些临时线程,如果这段时间都不忙那么就销毁这些临时线程, keepAliveTime用来规定不忙之后,临时线程的最大存活时间

4️⃣第四个参数:TimeUnit unit。
规定参数keepAliveTime的单位(毫秒,秒,分…)

5️⃣第五个参数:BlockingQueue workQueue,存放任务的阻塞队列。
线程池要管理很多任务,这些任务通过这个阻塞队列来组织, 程序员可以手动给线程池指定一个队列 ,就更加便于程序员控制获取队列中的信息,submit方法就是将任务放进这个队列里。

6️⃣第六个参数:ThreadFactory threadFactory。
这是一个工厂类,与之前的工厂模式类似,就是用来辅助线程池创建线程的辅助类

7️⃣第七个参数:RejectedExecutionHandler handler。
线程池的拒绝策略。 如果线程池满了,仍继续往里添加任务,如何拒绝添加


3.线程池的拒绝策略

标准库一共为线程池提供了四种拒绝策略
在这里插入图片描述


大前提:任务队列已经满了,还添加任务

  1. 第一种策略:AbortPolicy。
    直接抛出异常
  2. 第二种策略:CallerRunsPolicy。
    哪个线程添加的这个任务,就 让这个线程自己去执行这个任务
  3. 第三种策略:DiscardOldestPolicy。
    丢弃最老的任务 (阻塞队列中位于队首的任务)
  4. 第四种策略:DiscardPolicy。
    丢弃最新的任务 (阻塞队列中位于队尾的任务,也就是直接丢弃这个要添加的新任务)。

假设,你的任务列表容量大小为3,只能同时记住三件事,
1.第一件事:吃饭🍚
2.第二件事:打CSGO🎮
3.第三件事:敲代码🧑🏻‍💻
这时你的女朋友给你发微信,让你去帮她拿个快递。

四种拒绝策略分别会怎么做呢???

  1. 第一种策略,我让我自己嘎☠️,什么都不做了。
  2. 第二种策略,我记不住这第四件事了,女朋友让我去拿,她让我去,那就让她自己去拿。💢(这个任务谁添加的谁来执行)
  3. 第三种策略,😵丢弃吃饭(抛弃队首任务),记住帮女朋友拿快递
  4. 第四种策略,😵假装没看见微信,不管这个事,爱拿不拿。(抛弃队尾任务)

三、代码模拟实现线程池

public class MyThreadPool {

    // 阻塞队列存放任务
    private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();

    // 存放任务的方法
    public void subMit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }


    // 此处实现一个固定线程数的线程池
    public MyThreadPool (int n) {
        for (int i = 0; i < n; i++) {
            Thread thread = new Thread(()->{
                try {
                    while (true) {
                        // 此处需要让每个线程内部都有 while 循环, 
                        // 不停的从阻塞队列当中取任务,执行任务
                        Runnable runnable = queue.take();
                        runnable.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }
}

总结

以上就是今天要讲的内容,本文介绍了线程池及其使用方法,更介绍了其中重要的四种拒绝策略,还用代码模拟实现了线程池而,今天的内容理论较多,代码实现比较简单,望大家慢慢品味。

路漫漫不止修身,也养性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值