【源码分析】之 线程池工具类 Executors详解

news2024/9/21 16:40:07


快捷导航

  • 一、提供了什么功能?
    • 源码中的定义:
    • 此类支持以下几种方法:
  • 二、源码中是怎么实现的?
    • 1、创建并返回一个配置了常用设置的`ExecutorService`
      • newFixedThreadPool()
      • newSingleThreadExecutor()
      • newCachedThreadPool()
      • newWorkStealingPool()
    • 2、创建并返回一个`ScheduledExecutorService`
      • newScheduledThreadPool()
      • newSingleThreadScheduledExecutor()
    • 3、创建并返回一个禁用重新配置的`ExecutorService`
      • unconfigurableExecutorService()
      • unconfigurableScheduledExecutorService()
    • 4、创建并返回一个`ThreadFactory`
      • defaultThreadFactory()
      • privilegedThreadFactory()
    • 5、创建并返回一个`Callable`
      • callable()
  • 三、总结:

一、提供了什么功能?

源码中的定义:

该类为ExecutorExecutorServiceScheduledExecutorServiceThreadFactoryCallable类定义了工厂和工具方法。

此类支持以下几种方法:

  • 创建并返回一个配置了常用设置的ExecutorService的方法。
  • 创建并返回一个配置了常用设置的ScheduledExecutorService的方法。
  • 创建并返回一个“包装”的ExecutorService的方法,该方法通过使实现特定的方法不可访问来禁用重新配置。
  • 创建并返回一个将新创建线程设置为已知状态的ThreadFactory的方法。
  • 创建并返回一个Callable的方法,该方法基于其他闭包形式,以便可以在需要Callable的执行方法中使用。

说了这么多的名词,那么这些名词之间的关系是什么?请看下图:
​​​​在这里插入图片描述

二、源码中是怎么实现的?

我们先来看看Executors类的结构:

在这里插入图片描述

1、创建并返回一个配置了常用设置的ExecutorService

newFixedThreadPool()

    /**
     * 创建一个线程池,该线程池重用固定数量的线程,并使用共享的无界队列进行操作。
     * 在任何时候,最多将有 nThreads 个线程处于活动状态以处理任务。
     * 如果在所有线程都处于活动状态时提交了额外的任务,这些任务将等待在队列中,直到有线程可用。
     * 如果在执行过程中任何线程因故障而终止,在关闭之前,如果需要执行后续任务,将会有一个新线程替代它。
     * 线程池中的线程将一直存在,直到显式关闭。 
     *
     * 参数: 
     * nThreads – 线程池中的线程数量 
     *
     * 抛出: 
     * IllegalArgumentException – 如果 nThreads <= 0
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

	/**
     * 使用指定线程工厂创建新线程的重载方法
     *
     * 参数:
     * nThreads – 线程池中的线程数量
     * threadFactory – 创建新线程时使用的工厂
     *
     * 抛出:
     * NullPointerException – 如果threadFactory为null
     * IllegalArgumentException – 如果nThreads <= 0
     */
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

newSingleThreadExecutor()

	/**
     * 从代码上可以看出,该方法和newFixedThreadPool()大同小异,只有两点不同:
     * 1. 线程池核心线程数不同,newFixedThreadPool的核心线程数是方法入参nThreads,而本方法的核心线程数是1。
     * 2. 本方法在new ThreadPoolExecutor()之外加了一层封装new FinalizableDelegatedExecutorService()。
     *    只提供了本类对接口ExecutorService实现的方法的访问接口,目的是防止ThreadPoolExecutor实例在某些情况下	对线程池配置的修改,
     *    例如:使用setCorePoolSize()重新设置线程池核心线程数
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    // 使用指定线程工厂创建新线程的重载方法
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

newCachedThreadPool()

	/**
     * 创建一个线程池,该线程池根据需要创建新线程,但会在可用时重用之前构造的线程。
     * 这些线程池通常会提高执行许多短暂异步任务的程序的性能。
     * 调用execute时,如果有可用的线程,将重用之前构造的线程。
     * 如果没有现有线程可用,将创建一个新线程并添加到池中。
     * 未使用超过六十秒的线程将被终止并从缓存中移除。因此,长时间保持空闲的池不会消耗任何资源。
     * 请注意,可以使用ThreadPoolExecutor构造函数创建具有类似属性但不同细节(例如超时参数)的池。
     *
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    // 使用指定线程工厂创建新线程的重载方法
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }    

newWorkStealingPool()

    /**
     * 创建一个线程池,该线程池维护足够的线程以支持给定的并行级别,并可能使用多个队列来减少竞争。
     * 并行级别对应于主动参与或可用于任务处理的最大线程数。
     * 实际线程数量可能会动态增长和缩小。
     * 工作窃取池不保证提交任务的执行顺序。 
     *
     * 参数: 
     * parallelism – 目标并行级别 
     *
     * 抛出: 
     * IllegalArgumentException – 如果 parallelism <= 0 
     */
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }     
    // 创建一个工作窃取线程池,使用所有可用处理器作为其目标并行级别。
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

2、创建并返回一个ScheduledExecutorService

newScheduledThreadPool()

	/**
     * 创建一个可以在给定延迟后运行或定期执行的线程池。 
     *
     * 参数: 
     * corePoolSize – 即使线程处于空闲状态,也要保持在池中的线程数量 
     *
     * 抛出: 
     * IllegalArgumentException – 如果 corePoolSize < 0
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    // 使用指定线程工厂创建新线程的重载方法
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

newSingleThreadScheduledExecutor()

	// 返回一个类似newScheduledThreadPool(1)的单线程执行器,在任何情况下线程数量都不可更改
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }
	// 使用指定线程工厂创建新线程的重载方法
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }

3、创建并返回一个禁用重新配置的ExecutorService

unconfigurableExecutorService()

	// 和newSingleThreadExecutor()、newSingleThreadScheduledExecutor()类似的套路
	// 添加一个包装类,屏蔽返回的线程池对象对配置的修改
    public static ExecutorService unconfigurableExecutorService(ExecutorService executor) {
        if (executor == null)
            throw new NullPointerException();
        return new DelegatedExecutorService(executor);
    }

unconfigurableScheduledExecutorService()

	// 和unconfigurableExecutorService同样的套路
    public static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) {
        if (executor == null)
            throw new NullPointerException();
        return new DelegatedScheduledExecutorService(executor);
    }

4、创建并返回一个ThreadFactory

defaultThreadFactory()

    public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
    }
    
    // 静态内部类DefaultThreadFactory
    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

privilegedThreadFactory()

    
    public static ThreadFactory privilegedThreadFactory() {
        return new PrivilegedThreadFactory();
    }
    
    /**
     * Thread factory capturing access control context and class loader
     */
    static class PrivilegedThreadFactory extends DefaultThreadFactory {
        private final AccessControlContext acc;
        private final ClassLoader ccl;

        PrivilegedThreadFactory() {
            super();
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                // Calls to getContextClassLoader from this class
                // never trigger a security check, but we check
                // whether our callers have this permission anyways.
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);

                // Fail fast
                sm.checkPermission(new RuntimePermission("setContextClassLoader"));
            }
            this.acc = AccessController.getContext();
            this.ccl = Thread.currentThread().getContextClassLoader();
        }

        public Thread newThread(final Runnable r) {
            return super.newThread(new Runnable() {
                public void run() {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            Thread.currentThread().setContextClassLoader(ccl);
                            r.run();
                            return null;
                        }
                    }, acc);
                }
            });
        }
    }

5、创建并返回一个Callable

callable()


    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

    public static Callable<Object> callable(Runnable task) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<Object>(task, null);
    }

    public static Callable<Object> callable(final PrivilegedAction<?> action) {
        if (action == null)
            throw new NullPointerException();
        return new Callable<Object>() {
            public Object call() { return action.run(); }};
    }

    public static Callable<Object> callable(final PrivilegedExceptionAction<?> action) {
        if (action == null)
            throw new NullPointerException();
        return new Callable<Object>() {
            public Object call() throws Exception { return action.run(); }};
    }

    public static <T> Callable<T> privilegedCallable(Callable<T> callable) {
        if (callable == null)
            throw new NullPointerException();
        return new PrivilegedCallable<T>(callable);
    }

    public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) {
        if (callable == null)
            throw new NullPointerException();
        return new PrivilegedCallableUsingCurrentClassLoader<T>(callable);
    }

三、总结:

  1. 通过对以上源码的分析,可以发现虽然Executors工具类提供了很多快速创建线程池的方法,但归根结底还是对ThreadPoolExecutor、ScheduledThreadPoolExecutor的封装。我们可以很容易的根据自己的项目情况去自定义,这样的创建的线程池才是最符合业务场景的。
  2. 在Executors提供的快速创建线程池的方法中,若有贴合使用业务场景的,可以直接使用,这样可以提高开发效率,避免重复造轮子。
  3. Executors提供了从Runable到Callable的转换,这可能在需要线程提供返回值的时候是有用的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1974330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA项目基于SSM的教师管理系统

目录 一、前言 二、技术介绍 三、项目实现流程 四、论文流程参考 五、核心代码截图 专注于大学生实战开发、讲解和毕业答疑等辅导&#xff0c;获取源码后台 一、前言 随着教育事业的蓬勃发展&#xff0c;教师作为教育工作的核心力量&#xff0c;其管理的高效性、科学性日…

十大人力资源SAAS软件:企业管理的革新者

本文将介绍以下10款工具&#xff1a;Moka、北森云计算、智能人事、蓝凌OA、人瑞人才、Rippling、Sage HR、Deel、Gusto、TriNet。 在管理人力资源时&#xff0c;选择正确的工具至关重要。市场上的众多SAAS软件选项可能会让你感到不知所措&#xff0c;特别是在试图找到能够提升团…

RMAN-06618不同版本之间RMAN无法连接

RMAN Active Duplicate Between Two Oracle Versions (Doc ID 2346507.1)​编辑To Bottom In this Document Goal Solution References APPLIES TO: Oracle Database Cloud Schema Service - Version N/A and later Oracle Database Exadata Cloud Machine - Version N/A and…

接口测试知识点1

接口测试 软件接口&#xff0c;是指软件不同模块之间交互的接口&#xff0c;我们通常所说的API&#xff08;Application Programming Interface 应用程序接口&#xff09;&#xff0c;即是软件系统不同模块之间衔接的约定。 接口测试即是对软件各个模块的接口进行的测试。 接…

深入理解 HTTP 状态码

HTTP 状态码是服务器在收到客户端请求后返回的响应代码&#xff0c;用来表示请求的处理结果。这些状态码帮助用户理解请求是否成功以及服务器的响应状态。HTTP 状态码分为五大类&#xff0c;每一类都有特定的含义。下面&#xff0c;我们来详细解读这些状态码&#xff0c;帮助你…

前端的学习-CSS(弹性布局-flex)

一&#xff1a;什么是弹性布局-Flex flex 是 Flexible Box 的缩写&#xff0c;意为"弹性布局"&#xff0c;用来为盒状模型提供最大的灵活性。 语法&#xff1a; .box{display: flex; } .box{display: inline-flex; } 注意&#xff0c;设为 Flex 布局以后&#xff0…

MySQL之delete 、truncate与drop区别

快速使用 drop&#xff1a;‌删除表及其所有数据【drop 表名】 truncate&#xff1a;‌仅删除表中的所有数据【truncate 表名】 delete&#xff1a;‌删除表中的特定行数据&#xff0c;‌可以指定条件删除 【delete from 表名】 1、功能差异 drop&#xff1a;‌删除表及其所有…

【Yolov8】实战三:手把手教你使用YOLOv8以及pyqt搭建中医耳穴辅助诊断项目原理及模型部署(下)

今天&#xff0c;学习RTMPose关键点检测实战。教大家如何安装安装MMDetection和MMPose。 实战项目以三角板关键点检测场景为例&#xff0c;结合OpenMMLab开源目标检测算法库MMDetection、开源关键点检测算法库MMPose、开源模型部署算法库MMDeploy&#xff0c;全面讲解项目全流程…

基于SpringBoot+Vue的校车调度管理系统(带1w+文档)

基于SpringBootVue的校车调度管理系统(带1w文档) 基于SpringBootVue的校车调度管理系统(带1w文档) 如今&#xff0c;因为无线网相关技术的快速&#xff0c;尤其是在网上进行资源的上传下载、搜索查询等技术&#xff0c;以及信息处理和语言开发技术的进步&#xff0c;同时编程语…

基于51单片机设计的温湿度采集检测系统仿真源码文档视频——文末资料下载

演示 基于51单片机设计的温湿度采集检测系统仿真&源码&文档视频——资料下载见简介 目录 1.系统功能 2.背景介绍 3.硬件电路设计 4.软件设计 4.1 主程序设计 4.2 温湿度采集模块程序设计 4.3 LCD显示屏程序设计 5.系统测试 6.结束语 源码、仿真、文档视频等资…

捉虫笔记(二)之 杀软请你自重点

捉虫笔记&#xff08;二&#xff09;之 杀软请你自重点 前一篇文章介绍了如何配置符号&#xff0c;这一篇文章我们来个实战。 1 现象 在我们的程序中利用robocopy进行文件的复制。但是QA反馈&#xff0c;只要进行了备份操作&#xff0c;整个进程就会卡住。但是奇怪的是只有他…

NIO专题学习(一)

一、BIO/NIO/AIO介绍 1. 背景说明 在Java的软件设计开发中&#xff0c;通信架构是不可避免的。我们在进行不同系统或者不同进程之间的数据交互&#xff0c;或者在高并发的通信场景下都需要用到网络通信相关的技术。 对于一些经验丰富的程序员来说&#xff0c;Java早期的网络…

前端 HTML 概述

目录 1. HTML概述 1.1 超文本标记语言 1.2 标签 2. HTML 解析与编辑 2.1 解析与访问 2.2 编辑 html文件 1. HTML概述 HTML&#xff08; Hyper Text Markup Language&#xff1a;超文本标记语言 &#xff09;&#xff1a;主要用于网页主体结构的搭建&#xff0c;在网页上…

维修雅萌五代射频仪

维修雅萌五代射频仪&#xff0c;主板进水&#xff0c;看起来有点严重&#xff0c;看看这回能不能把它修好

MySQL--数据库索引

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 MySQL数据库--索引 索引是对数据库表中一列或多列的值进行排序的一种结构&#xff0c;使用索引可提高数据库中特定数据的查询速度 一、索引简介 1、…

python实现小游戏——植物大战僵尸(魔改版本)

制作一款DIY的‘植物大战僵尸’游戏引起了很多人的兴趣。在这里&#xff0c;我将分享一个使用Python语言在PyCharm环境中开发的初始状态版本。这个版本主要应用了pygame库来完成&#xff0c;是一个充满创意和趣味的魔改版本。 文章目录 前言一、开发环境准备二、代码1.main方法…

Unbuntu 服务器- Anaconda安装激活 + GPU配置

一、Anaconda安装激活 1.更新 sudo apt-get update 2.安装wget、vim sudo apt-get install wget sudo apt-get install vim 3.安装Anaconda 进入这个网址&#xff1a;Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 点这里&#x…

【每日一题】【技巧】【LeetCode热题 100】【力扣】75. 颜色分类 C++

力扣75. 颜色分类 75. 颜色分类 题目描述 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 …

永久旋转 PDF 文件的 2 种简便方法

PDF 文件通常由扫描仪创建&#xff0c;用于呈现文档或书籍。当您输出 PDF 作品时&#xff0c;打开它&#xff0c;会发现有几页是颠倒的。 你该怎么办&#xff1f; 将这些页面倒置扫描&#xff0c;按顺序排列&#xff0c;最后创建正确的 PDF 文件&#xff1f; 当然&#xff0…

暑假第三周任务——天气预报

暑假第三周任务——天气预报 文章目录 暑假第三周任务——天气预报前言URL与APIAPI与URL的关系 获取网络请求首页搜索界面详情界面添加功能 浏览界面总结 前言 这个天气预报主要是通过申请网络请求来获取实时数据&#xff0c;来实现一个天气预报的功能&#xff0c;在这里主要是…