<JavaEE> 经典设计模式之 -- 线程池

这篇具有很好参考价值的文章主要介绍了<JavaEE> 经典设计模式之 -- 线程池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、线程池的概念

二、Java 标准库中的线程池类

2.1 ThreadPoolExecutor 类

2.1.1 corePoolSize 和 maximumPoolSize

2.1.2 keepAliveTime 和 unit

2.1.3 workQueue

2.1.4 threadFactory

2.1.5 handler

2.1.6 创建一个参数自定义的线程池

2.2 Executors 类

2.3 实现自己的线程池


一、线程池的概念

1)什么是线程池?

准备预期需要使用的对象,将这些对象放入一个可以随时取用的容器中,这个容器就被称为“池”。

使用过的对象也不立刻销毁,而是放回“池”中,以备下次使用。

将线程作为上述对象,放在一个容器中,这就称为“线程池”。

2)为什么使用线程池?
在实际使用中,“线程池”并没有频繁的创建和销毁线程,而是从“池”中存取线程,这样可以减少每次启动和销毁线程的损耗,提高运行效率,减小系统开销。
3)为什么使用线程池可以提高效率?

通常创建线程,是通过向系统申请创建,需要通过系统内核完成,是具有内核态的代码。

而从线程池中存取线程,是属于用户态的代码,相比于内核态代码更可控、稳定,操作更快。


二、Java 标准库中的线程池类

2.1 ThreadPoolExecutor 类

这个类的构造方法参数众多,包含以下参数:

int corePoolSize

核心线程数

int maximumPoolSize

最大线程数

long keepAliveTime

空闲线程存活时间

TimeUnit unit

时间单位

BlockingQueue<Runnable> workQueue

任务队列

ThreadFactory threadFactory

线程工厂

RejectedExecutionHandler handler

拒绝策略

2.1.1 corePoolSize 和 maximumPoolSize

int corePoolSize 核心线程数

线程池中保持持有的最小线程数量。

int maximumPoolSize 最大线程数

线程池中可以持有的最大线程数量。
标准库提供的线程池,持有的线程数量是可以变动的,可以根据需要执行的任务数量,自适应线程的数量。

2.1.2 keepAliveTime 和 unit

long keepAliveTime 空闲线程存活时间

当线程池中的线程处于空闲状态时,会等待 keepAliveTime 时间后自动关闭。

如果keepAliveTime为0,线程在执行完任务后则不会关闭。

TimeUnit unit 时间单位
keepAliveTime 空闲线程存活时间的时间单位。

2.1.3 workQueue

BlockingQueue<Runnable> workQueue 任务队列

用于存储等待执行的任务。当线程池中的线程数量达到最大值时,新任务将被添加到队列中等待执行。

执行的任务应该是 Runnable 接口的实现类,

2.1.4 threadFactory

前置知识点:工厂模式

什么是工厂模式?

工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种在不指定具体类的情况下创建对象的接口。

工厂模式的核心思想是,将对象的创建过程抽象出来,使得使用者的创建与类的实例化过程解耦。

在工厂模式中,会实现一个工厂类,它提供一个创建对象的接口,而使用者只需要调用这个接口即可,工厂类会根据客户端的请求,返回不同类型的对象。

threadFactory 具体指什么:

ThreadFactory threadFactory 线程工厂
通过工厂类创建线程对象(简单理解就是通过这个类产出线程)。ThreadFactory 是一个接口,接口中只中有一个方法 newThread 方法,通过覆写该方法获得一个线程,同时可以给这个新线程设置一些属性。

2.1.5 handler

RejectedExecutionHandler handler 拒绝策略
线程池异常处理器,也称拒绝策略。这里的 handler 表示一个实现了 RejectedExecutionHandler 接口的实现类,传入的 handler 参数决定了出现异常时,线程池如何处理异常。
有哪几种拒绝策略,分别表示什么?
ThreadPoolExecutor.AbortPolicy 当线程池已满,且阻塞队列已满,新任务无法执行,则立即抛出 RejectedExecutionException 异常,是默认策略。
ThreadPoolExecutor.CallerRunspolicy 当线程池已满,且阻塞队列已满,新任务无法执行,则由调用线程自己执行新任务。
ThreadPoolExecutor.DiscardOldestPolicy 当线程池已满,且阻塞队列已满,新任务无法执行,则丢弃最早添加的任务,然后添加新任务。
ThreadPoolExecutor.DiscardPolicy 当线程池已满,且阻塞队列已满,新任务无法执行,则丢弃新任务,不执行。

2.1.6 创建一个参数自定义的线程池

创建并运行以下线程池和任务:

<JavaEE> 经典设计模式之 -- 线程池,JavaEE,设计模式,多线程,java-ee

<JavaEE> 经典设计模式之 -- 线程池,JavaEE,设计模式,多线程,java-ee

分析上述代码运行结果:

<JavaEE> 经典设计模式之 -- 线程池,JavaEE,设计模式,多线程,java-ee

2.2 Executors 类

1)简单介绍 Executors 类

ThreadPoolExecutor 类参数较多,使用较复杂。因此,为方便使用,标准库中又提供了 Executors 工厂类。

Executors 类是对 ThreadPoolExecutor 类进行了一层封装,是一个工厂类,通过这个类创建出不同属性的线程池对象。
Executors 类返回的是一个 ExecutorService 类型的线程池实例。通过 ExecutorServic.submit 可以向线程池中添加任务。
2)简单介绍常用的 Executors 类创建线程池的方法

newFixedThreadPool

创建固定线程数的线程池

newCachedThreadPool

创建线程数目动态增长的线程池

newSingleThreadExecutor

创建只包含单个线程的线程池

newScheduledThreadPool

创建可以定时执行任务的线程池
3)开发过程中应该使用 ThreadPoolExecutor 类还是使用 Executors 类?

ThreadPoolExecutor 类参数较多、可以自定义,这意味着使用这个类创建的线程池更灵活。

而 Executors 类相比 ThreadPoolExecutor 类多了一层封装,部分参数已经设定好,这使得 Executors 类在使用上更便利。

这两种选择,前者更偏向于高度定制化的线程池,而后者偏向于通用性。两者并无高下之分,各有偏向,使用者根据实际应用场景使用即可。

2.3 实现自己的线程池

线程池的使用有以下关键点:

<1>

线程池,肯定要有线程。在这里实现一个线程数量固定的线程池,就需要在这个类的构造方法中,根据输入的参数 n ,新建 n 个线程。
<2> 维护一个阻塞队列,队列持有需要执行的任务。
<3> 提供一个 submit 方法,用于将任务添加到任务队列中。
<4> 线程池中的线程,也有自己的任务,就是不断地扫描任务队列,如果队列中有任务,则取出任务后执行。
<5> 注意新建的线程需要有合适的启动时机。在以下实现中,我们让线程一创建就启动。

代码演示线程池的实现:

class MyThreadPool{
    //创建一个用于存放任务的队列;
    private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(5);

    //构造方法,用于构造线程池。方法中需要将线程new出来;
    public MyThreadPool(int n){
        //设置线程需要执行的任务;
        Runnable runnable = new  Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        //从任务队列中取出任务,并执行;
                        queue.take().run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        //循环创建线程,并启动,将线程放入队列中;
        for (int i = 0; i < n; i++){
            Thread t = new Thread(runnable);
            t.start();
        }
    }

    //添加任务方法。往任务队列中添加任务;
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}
public class ThreadPool_Demo35 {
    public static void main(String[] args) throws InterruptedException {
        //新建线程池;
        MyThreadPool threadPool = new MyThreadPool(2);

        //给线程池添加任务;
        for (int i= 0; i < 10; i++){
            int n = i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 执行任务:" + n);
                }
            });
        }
    }
}


//运行结果:
Thread-0 执行任务:0
Thread-1 执行任务:1
Thread-0 执行任务:2
Thread-1 执行任务:3
Thread-0 执行任务:4
Thread-0 执行任务:6
Thread-0 执行任务:7
Thread-0 执行任务:8
Thread-0 执行任务:9
Thread-1 执行任务:5
...

十个任务全部成功打印。
应该注意到,线程没有执行结束,还在等待新的任务加入,这是线程池的合理功能。

阅读指针 -> 《 Synchronized 锁进阶 -- 锁策略》

<JavaEE> 锁进阶 -- 锁策略(乐观锁和悲观锁、重量级锁和轻量级锁、自旋锁和挂起等待锁、可重入锁和不可重入锁、公平锁和非公平锁、读写锁)-CSDN博客介绍了以下锁策略:乐观锁和悲观锁、重量级锁和轻量级锁、自旋锁和挂起等待锁、可重入锁和不可重入锁、公平锁和非公平锁、读写锁;https://blog.csdn.net/zzy734437202/article/details/134916407文章来源地址https://www.toymoban.com/news/detail-753581.html

到了这里,关于<JavaEE> 经典设计模式之 -- 线程池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Design Pattern 23种经典设计模式源码大全】C/Java/Go/JS/Python/TS等不同语言实现

    经典设计模式源码详解,用不同语言来实现,包括Java/JS/Python/TypeScript/Go等。结合实际场景,充分注释说明,每一行代码都经过检验,确保可靠。 设计模式是一个程序员进阶高级的必然选择,不懂设计模式,就像写文章不懂得层次,盖房子没有结构。只有充分懂得设计之道,

    2023年04月11日
    浏览(43)
  • 设计模式:智能合约的经典设计模式及解析

        苏泽 大家好 这里是苏泽 一个钟爱区块链技术的后端开发者 本篇专栏  ← 持续记录本人自学两年走过无数弯路的智能合约学习笔记和经验总结 如果喜欢拜托三连支持~ 总而言之,智能合约实现上要达到的目标是: 完备的业务功能、精悍的代码逻辑、良好的模块抽象、清

    2024年04月12日
    浏览(39)
  • 经典的设计模式21——策略模式

    猛的发现策略模式和状态模式的结构图长得到好像,可得好好区分区分。 不过真的好像,就是方法那里传递的参数不一样。 百度来一波。 定义: 定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。 策略模式属于

    2024年02月10日
    浏览(36)
  • 经典的设计模式13——模板方法模式

    开始11个属于行为型模式的复习。 用户登录控制功能用模板方法实现。 定义: 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 结构: 抽象类(abstract):负责给出一个算法的轮廓和骨架

    2024年02月08日
    浏览(40)
  • 经典的设计模式15——迭代器模式

    迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。 就是说当需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑用迭代器模式。需要对聚集有多种方式遍历时,可以考虑用迭代器模式,比如从头到尾遍

    2024年02月09日
    浏览(77)
  • 经典的设计模式17——中介者模式

    定义: 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。 结构: 抽象中介者角色:是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。 具体中介者角色:实现中

    2024年02月09日
    浏览(42)
  • 设计模式系列:经典的单例模式

    单例模式,是设计模式当中非常重要的一种,在面试中也常常被考察到。 正文如下: 一、什么时候使用单例模式? 单例模式可谓是23种设计模式中最简单、最常见的设计模式了,它可以保证一个类只有一个实例。我们平时网购时用的购物车,就是单例模式的一个例子。想一

    2024年02月15日
    浏览(45)
  • 经典的设计模式16——观察者模式

    定义: 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使得他们能够自动更新自己。 目标和观察者:一对多 单向依赖。通知的顺序绝不依赖于通知的顺序,多个观察者之间的功能是平行的

    2024年02月09日
    浏览(36)
  • 设计模式——经典单例

    // 构造、析构函数私有化(一个进程只允许一个对象存在) // 对象私有化、静态化(因为接口静态函数) // 对象调用接口静态化(因为静态函数脱离了类对象,可以直接调用) 唯一的对象在使用时才进行初始化。存在多线程问题。 唯一的对象在定义时就完成初始化。

    2024年02月12日
    浏览(38)
  • 【JAVA开发面试】如何处理并发访问如何进行代码的单元测试Java多线程编程消息中间件设计模式技术难题是如何解决的

    【 点我-这里送书 】 本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题 中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明

    2024年02月03日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包