浅谈线程池

news2024/11/23 17:06:59

浅谈线程池

1、线程池

1.1、线程池介绍

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

1.2、线程池的优点

线程池有如下优势:

  • 避免频繁创建和销毁线程带来的资源浪费
  • 无需创建和销毁线程,提升响应速度
  • 提高线程的可管理性

1.3、创建线程池的方式

创建线程的方式有如下两种:

  • ThreadPoolExecutor
  • Executors

2、ThreadPoolExecutor

点进ThreadPoolExecutor 构造方法有四个,但是前面三个都是在最后一个上进行的包装处理,所以这里只针对最后一个构造方法进行说明。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

先聊参数:

  • corePoolSize: 线程池中的核心线程数,默认情况下核心线程一直存活在线程池中,如果将ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设为 true,如果线程池一直闲置并超过了 keepAliveTime 所指定的时间,核心线程就会被终止。
  • maximumPoolSize: 最大线程数,当线程不够时能够创建的最大线程数(包含核心线程数)

临时线程数 = 最大线程数 - 核心线程数

  • keepAliveTime: 线程池的闲置超时时间,默认情况下对非核心线程生效,如果闲置时间超过这个时间,非核心线程就会被回收。如果 ThreadPoolExecutor 的 allowCoreThreadTimeOut 设为 true 的时候,核心线程如果超过闲置时长也会被回收。

  • unit: 配合 keepAliveTime 使用,用来标识 keepAliveTime 的时间单位,一般使用TimeUnit中的枚举常量,解释如下:

    TimeUnit.DAYS //天
    TimeUnit.HOURS //小时
    TimeUnit.MINUTES //分钟
    TimeUnit.SECONDS //秒
    TimeUnit.MILLISECONDS //毫秒
    TimeUnit.NANOSECONDS //毫微秒
    TimeUnit.MICROSECONDS //微秒

  • workQueue: 线程池中的任务队列,使用 execute() 或 submit() 方法提交的任务都会存储在此队列中。

    ThreadPoolExecutor线程池推荐了三种等待队列,它们是:SynchronousQueue 、LinkedBlockingQueue和 ArrayBlockingQueue。

    1. SynchronousQueue :一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于 阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。

    2. LinkedBlockingQueue:基于链表结构的无界阻塞队列,它可以指定容量也可以不指定容量(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)

    3. ArrayBlockingQueue:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

  • threadFactory: 为线程池提供创建新线程的线程工厂。如果不指定线程工厂时,ThreadPoolExecutor 会使用ThreadPoolExecutor.defaultThreadFactory 创建线程。默认工厂创建的线程:同属于相同的线程组,具有同为 Thread.NORM_PRIORITY 的优先级,以及名为 “pool-XXX-thread-” 的线程名(XXX为创建线程时顺序序号),且创建的线程都是非守护进程。

  • handler:表示当 workQueue 已满,且池中的线程数达到 maximumPoolSize 时,线程池拒绝添加新任务时采取的策略。(可以不指定)

    ThreadPoolExecutor 也提供了 4 种默认的拒绝策略:

    1. DiscardPolicy():丢弃掉该任务但是不抛出异常,不推荐这种(导致使用者没觉察情况发生)
    2. DiscardOldestPolicy():丢弃队列中等待最久的任务,然后把当前任务加入队列中。
    3. AbortPolicy():丢弃任务并抛出 RejectedExecutionException 异常(默认)。
    4. CallerRunsPolicy():由主线程负责调用任务的run()方法从而绕过线程池直接执行,既不抛弃 任务也不抛出异常(当最大线程数满了,任务队列中也满了,再来一个任务,由主线程执行)

示例:

// 新建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
        5,
        10,
        5,
        TimeUnit.MILLISECONDS,
        new ArrayBlockingQueue<>(5),
        new ThreadPoolExecutor.AbortPolicy()
);
// execute的使用
executor.execute(new Runnable() {
    @Override
    public void run() {
        System.out.println("hello, world!");
    }
});
// submit的使用
Future<String> submit = executor.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        System.out.println("hello, world!");
        return "hello, world!";
    }
});
System.out.println(submit.get());

另外,ThreadPoolExecutor 还提供了许多的get()/set()方法

请添加图片描述
请添加图片描述

3、Executors

Executors 执行器创建线程池很多基本上都是在 ThreadPoolExecutor 构造方法上进行简单的封装,特殊场景根据需要自行创建。可以把Executors理解成一个工厂类。Executors可以创建6 种不同的线程池类型。

下面对这六个方法进行简要的说明:

  • newFixedThreadPool: 创建一个数量固定的线程池,超出的任务会在队列中等待空闲的线程,可用于控制程序的最大并发数。

    // 只需要传入一个参数,该参数将被用作核心线程数以及最大线程数,其他参数将采用默认选项
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
  • newCacheThreadPool: 短时间内处理大量工作的线程池,会根据任务数量产生对应的线程,并试图缓存线程以便重复使用,如果限制 60 秒没被使用,则会被移除缓存。如果现有线程没有可用的,则创建一个新线程并添加到池中,如果有被使用完但是还没销毁的线程,就复用该线程。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
  • newScheduledThreadPool: 创建一个数量固定的线程池,支持执行定时性或周期性任务。

    // corePoolSize必传,threadFactory
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
    
  • newWorkStealingPool: Java 8 新增创建线程池的方法,创建时如果不设置任何参数,则以当前机器CPU 处理器数作为线程个数,此线程池会并行处理任务,不能保证执行顺序。

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
    
  • newSingleThreadExecutor: 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    // 可以指定线程工厂,也可以传入任何参数,将采用默认的线程工厂Executors.defaultThreadFactory()
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
    
  • newSingleThreadScheduledExecutor: 此线程池就是单线程的newScheduledThreadPool。

    // // 可以指定线程共从,也可以传入任何参数,将采用默认的线程工厂Executors.defaultThreadFactory()
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }
    

需要注意的是,在阿里巴巴Java开发手册中有这样的一条规定

请添加图片描述

也就是说,在实际上产中,应该尽量的避免用Excutors直接创建线程。

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

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

相关文章

Golang每日一练(leetDay0057) 缺失区间、最大间距

目录 163. 缺失的区间 Missing Ranges &#x1f31f;&#x1f31f; 164. 最大间距 Maximum Gap &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏…

空闲任务与阻塞延时(笔记)

目录 前言 空闲任务实现空闲任务1、定义空闲任务栈2、定义空闲任务的任务控制块4、定义空闲任务主体 实现阻塞延时vTaskDelay()函数任务与空闲任务切换的例子&#xff1a;vTaskSwitchContext()函数SysTick中断服务函数更新系统时基 SysTick初始化函数实验仿真 前言 软件延时是…

牛客网专项练习Pytnon分析库(二)

1.Z-score标准化公式&#xff0c;,中的σ表示的是什么&#xff08;C&#xff09;。 A.总体平均值 B.数据的方差 C.数据的标准差 D.数据的众数 解析&#xff1a; Z-score标准化也叫标准差标准化法&#xff0c;其中X表示数据样本值&#xff0c;μ表示数据样本的平均值&#x…

婚姻的本质,不是爱情

婚姻的本质&#xff0c;不是爱情 结婚是为了爱情么&#xff1f;普通人或许以为是&#xff0c;但实际并不是。如果你是为了爱情&#xff0c;那你不需要结婚。什么叫爱情。所谓爱情&#xff0c;就是你对她朝思暮想&#xff0c;时时刻刻都想和她在一起。而她也对你朝思暮想&#…

Vue学习笔记1 - Vue是什么?

1&#xff0c;Vue概念 官网上&#xff08;简介 | Vue.js&#xff09; 上说&#xff0c; Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。 这个还好理解&#xff0c;就是说它是一款前端框架&#xff0c;用于构建 前端界面的。 但是它…

NewBing 还无法访问的几个问题

大部分的AI自媒体都在说&#xff0c;Bing new已经向全世界开放了&#xff0c;我也凑一下这个热闹&#xff0c;用Edge浏览器打开&#xff0c;访问https://www.bing.com/new?ccus 想体验一下Bing new的效果&#xff0c;结果如下&#xff1a; 相信很多人都碰到了这个问题 此体验…

Windows上使用CLion配置OpenCV环境,CMake下载,OpenCV的编译,亲测可用的方法(一)

一、Windows上使用CLion配置OpenCV环境,亲测可用的方法: Windows上使用CLion配置OpenCV环境 教程里的配置: widnows 10 clion 2022.1.1 mingw 8.1.0 opencv 4.5.5 Cmake3.21.1 我自己的配置: widnows 10 clion 2022.2.5 mingw 8.1.0 https://sourceforge.net/projects/min…

二十三种设计模式第三篇--抽象工厂模式

介绍 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是围绕一个超级工厂创建其他工厂&#xff0c;该超级工厂又称为其他工厂的工厂。 这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 在抽象工厂模式中&#xff0c;接口是负责…

对标世界一流|亚马逊供应链管理经验借鉴

当前电商零售行业竞争日趋激烈&#xff0c;服务标准的提升、产品价格的竞争力等因素&#xff0c;导致企业经营成本持续上升&#xff0c;供应链的管理水平已经成为零售行业成败的关键。然而在电商零售行业的红海竞争中&#xff0c;亚马逊却始终保持着高速增长的态势&#xff0c;…

港联证券|4连板的AI+传媒概念股火了,近5亿资金抢筹

今天&#xff0c;沪深两市共51股涨停&#xff0c;除掉10只ST股&#xff0c;合计41股涨停。别的&#xff0c;11股封板未遂&#xff0c;全体封板率为81%。 涨停战场&#xff1a;长江传媒封单量最高 从收盘涨停板封单量来看&#xff0c;长江传媒封单量最高&#xff0c;有39.96万手…

STL初识

什么是STL? 菜鸟教程的解释是&#xff1a;C STL&#xff08;标准模板库&#xff09;是一套功能强大的 C 模板类&#xff0c;提供了通用的模板类和函数&#xff0c;这些模板类和函数可以实现多种流行和常用的算法和数据结构&#xff0c;如向量、链表、队列、栈。 也就是说&am…

4.1 数据结构引入

目录 什么是数据结构 语言出生顺序 基本概念 数据的逻辑结构 数据的存储结构 顺序储存 链式存储 索引存储 散列存储 基本概念 第一卷《基本算法》 第二卷《半数字化算法》 第三卷《排序与搜索》 第四卷《组合算法》 什么是数据结构 数据结构研究计算机数据间关系…

每日学术速递5.5

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CL 1.ResiDual: Transformer with Dual Residual Connections 标题&#xff1a;ResiDual&#xff1a;具有双剩余连接的Transformer 作者&#xff1a;Shufang Xie, Huishuai Zhang, Jun…

制造企业选择库存管理条码工具需要关注哪些点?

Dynamsoft Barcode Reader SDK 一款多功能的条码读取控件&#xff0c;只需要几行代码就可以将条码读取功能嵌入到Web或桌面应用程序。这可以节省数月的开发时间和成本。能支持多种图像文件格式以及从摄像机或扫描仪获取的DIB格式。使用Dynamsoft Barcode Reader SDK&#xff0c…

OpenCV实战(22)——单应性及其应用

OpenCV实战&#xff08;22&#xff09;——单应性及其应用 0. 前言1. 单应性1.1 单应性基础1.2 计算两个图像之间的单应性1.3 完整代码 2. 检测图像中的平面目标2.1 特征匹配2.2 完整代码 小结系列链接 0. 前言 我们已经学习了如何从一组匹配项中计算图像对的基本矩阵。在射影…

读论文《大气压等离子体电离波沿介质管传输特性研究》

文章目录 一、研究背景和意义二、研究目的与内容三、电离波概述3.1 电离波与传统的流注放电3.2 电离波传输速度的计算方法 四、放电参数对电离波传输特性的影响4.1 施加电压与电压波形对电离波传输的影响4.1.1 交流高压对电离波的影响4.1.2 脉冲高压对电离波的影响![在这里插入…

《编程思维与实践》1047.Base64编码

《编程思维与实践》1047.Base64编码 题目 思路 直接模拟:将每个Base64编码值都分为两部分:前半部分由上一个字符求得,后半部分由下一个字符求得. 特别地,如果字符为第一个或最后一个,则直接可以求得Base64编码. 如下图: 其中,% 2 n 2^n 2n表示取出后n位的二进制位, 这是因…

专业游戏录屏软件Camtasia 2023强悍来袭,Camtasia Studio 2023的新增功能!

Camtasia Studio 2023是一款专门录制屏幕动作的工具&#xff0c;它能在任何颜色模式下轻松地记录 屏幕动作&#xff0c;包括影像、音效、鼠标移动轨迹、解说声音等等&#xff0c;另外&#xff0c;它还具有即时播放和编 辑压缩的功能&#xff0c;可对视频片段进行剪接、添加转场…

又一起数据泄露事件五个月内的第二次

据报道&#xff0c;T-Mobile 在发现攻击者从 2023 年 2 月下旬开始的一个多月内访问了数百名客户的个人信息后&#xff0c;披露了 2023 年的第二次数据泄露事件。 与之前报告的数据泄露事件&#xff08;最近一次影响了 3700 万人&#xff09;相比&#xff0c;此次事件仅影响了…

Linux一学就会——编写自己的shell

编写自己的shell 进程程序替换 替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行…