Java笔记之线程池详解

news2024/11/16 11:26:59

文章目录

  • 一、线程池是什么?
  • 二、为什么要使用线程池?
  • 三、jdk自带的四种线程池
    • 1. 线程池参数
    • 2.工作队列
    • 3.拒绝策略
    • 4.四种线程池一些示例
  • 四、自定义线程池


一、线程池是什么?

一种线程使用模式,是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。

二、为什么要使用线程池?

(1) 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2) 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3) 提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

三、jdk自带的四种线程池

  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。

简单使用,代码如下(示例):

public class UseExecutors {
    public static void main(String[] args) {
        Runnable taskOne = () -> {
            ThreadUtils.sleep(1000);
            System.out.println(Thread.currentThread().getName()+":taskOne");
        };
        // ExecutorService pools = Executors.newCachedThreadPool();
        // ExecutorService pools = Executors.newSingleThreadExecutor();
        // ExecutorService pools = Executors.newScheduledThreadPool(10);
        ExecutorService pools = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 40; i++) {
            pools.submit(taskOne);
        }
    }
}

无论是哪一个,都调用ThreadPoolExecutor构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

1. 线程池参数

线程池有七个参数,意义如下:

corePoolSize指定了线程池里的线程数量,核心线程池大小
maximumPoolSize指定了线程池里的最大线程数量
keepAliveTime当线程池线程数量大于corePoolSize时候,多出来的空闲线程,多长时间会被销毁
unit时间单位,TimeUnit
workQueue任务队列,用于存放提交但是尚未被执行的任务
threadFactory线程工厂,用于创建线程,线程工厂就是给我们new线程的
handler所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略

2.工作队列

常见的工作队列我们有如下选择,这些都是阻塞队列,阻塞队列的意思是,当队列中没有值的时候,取值操作会阻塞,一直等队列中产生值。

ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。
LinkedBlockingQueue:基于链表结构的无界阻塞队列,FIFO。

3.拒绝策略

线程池提供了四种拒绝策略:
• AbortPolicy:直接抛出异常,默认策略;
• CallerRunsPolicy:用调用者所在的线程来执行任务;
• DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务;
• DiscardPolicy:直接丢弃任务;

线程池中,有三个重要的参数,决定影响了拒绝策略:

corePoolSize - 核心线程数,也即最小的线程数。
workQueue - 阻塞队列 。
maximumPoolSize - 最大线程数

当提交任务数大于 corePoolSize 的时候,会优先将任务放到 workQueue 阻塞队列中。当阻塞队列饱和后,会扩充线程池中线程数,直到达到 maximumPoolSize 最大线程数配置。此时,再多余的任务,则会触发线程池的拒绝策略了。也就是说当提交的任务数大于(workQueue.size() + maximumPoolSize ),就会触发线程池的拒绝策略。

线程池执行任务流程如下:

在这里插入图片描述

4.四种线程池一些示例

  1. newCachedThreadPool

示例代码

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
 核心线程池大小=0
 最大线程池大小为Integer.MAX_VALUE
 线程过期时间为60s
 使用SynchronousQueue作为工作队列.

所以线程池为0-max个线程,并且会60s过期,实现了可以缓存的线程池。

  1. newFixedThreadPool

示例代码

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
核心线程池大小=传入参数
最大线程池大小为传入参数
线程过期时间为0ms
LinkedBlockingQueue作为工作队列.

通过最小与最大线程数量来控制实现定长线程池.

  1. newScheduledThreadPool

示例代码

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
核心线程池大小=传入参数
最大线程池大小为Integer.MAX_VALUE
线程过期时间为0ms
DelayedWorkQueue作为工作队列.

主要是通过DelayedWorkQueue来实现的定时线程。

  1. newSingleThreadExecutor

示例代码

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
核心线程池大小=1
最大线程池大小为1
线程过期时间为0ms
LinkedBlockingQueue作为工作队列.

四、自定义线程池

这里是针对JDK1.8版本,使用JDK自带的线程池会出现OOM问题,
在这里插入图片描述
中小型公司一般很难遇到,在阿里巴巴开发文档上面有明确的标识:
在这里插入图片描述上边我们已经分析了线程池的几个参数,这几个参数核心线程数、最大线程数、活跃时间和单位根据服务器本身的性能和程序的特性设定。但是线程工厂、决绝策略、阻塞队列又该怎么搞呢?

拒绝策略其实很简单,ExecutorService构造时可以不传递拒绝策略,默认使用异常抛出的方式。

阻塞队列我们搞一个定长的队列就好了,ArrayBlockingQueue<>(DEFAULT_SIZE)

线程工厂的获取我们可以使用以下的方法:

第一种办法,看看原生的怎么搞一个线程工厂:

在这里插入图片描述进入看他的源码:

private 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() {
            @SuppressWarnings("removal")
            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;
        }
    }

线程工厂就是创建线程的,这里用到了一种设计模式,叫工厂设计模式。我们可以按照他的方式自己写一个.

public class MyThreadFactory implements ThreadFactory {

//    @Override
//    public Thread newThread(Runnable r) {
//        return new Thread(r);
//    }
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        @SuppressWarnings("removal")
        MyThreadFactory(String name) {
//            @SuppressWarnings("removal")
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            namePrefix = name + "-" +
                    poolNumber.getAndIncrement() +
                    "-thread-";
        }

        MyThreadFactory(){
            this("default");
        }

        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;
        }
}

第二种:Google guava 工具类 提供的 ThreadFactoryBuilder 。

在这里插入图片描述

ThreadFactory guavaThreadFactory = new ThreadFactoryBuilder().setNameFormat("retryClient-pool-").build();

第三种:Apache commons-lang3 提供的 BasicThreadFactory。

ThreadFactory basicThreadFactory = new BasicThreadFactory.Builder()
        .namingPattern("basicThreadFactory-").build();

因此,定义一下线程池示例:

public class AsyncProcessor {

    /**
     * 默认最大并发数<br>
     */
    private static final int DEFAULT_MAX_CONCURRENT = Runtime.getRuntime().availableProcessors() * 2;

    /**
     * 线程池名称格式
     */
    private static final String THREAD_POOL_NAME = "kf1-log-%d";

    /**
     * 线程工厂名称
     */
    private static final ThreadFactory FACTORY = new BasicThreadFactory.Builder().namingPattern(THREAD_POOL_NAME)
            .daemon(true).build();

    /**
     * 默认队列大小
     */
    private static final int DEFAULT_SIZE = 500;

    /**
     * 默认线程存活时间
     */
    private static final long DEFAULT_KEEP_ALIVE = 60L;

    /**
     * NewEntryServiceImpl.java:689
     * Executor
     */
    private static ExecutorService executor;

    /**
     * 执行队列
     */
    private static BlockingQueue<Runnable> executeQueue = new ArrayBlockingQueue<>(DEFAULT_SIZE);

    static {
        executor = new ThreadPoolExecutor(
                DEFAULT_MAX_CONCURRENT,
                DEFAULT_MAX_CONCURRENT * 4,
                DEFAULT_KEEP_ALIVE,
                TimeUnit.SECONDS,
                executeQueue,
                FACTORY);

    }

    /**
     * 此类型无法实例化
     */
    private AsyncProcessor() {
    }
    
    public static boolean executeTask(Runnable task) {
        try {
            executor.execute(task);
        } catch (RejectedExecutionException e) {
            System.out.println("Task executing was rejected.");
            return false;
        }
        return true;
    }

    /**
     * 提交任务,并可以在稍后获取其执行情况<br>
     * 当提交失败时,会抛出 {@link }
     * @param task
     * @return
     */
    public static <T> Future<T> submitTask(Callable<T> task) {

        try {
            return executor.submit(task);
        } catch (RejectedExecutionException e) {
            throw new UnsupportedOperationException("Unable to submit the task, rejected.", e);
        }
    }
}

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

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

相关文章

FineReport表格软件-CSS动态切换年月日查询报表

1. 概述 1.1 版本 报表服务器版本 功能变更 11.0 -- 1.1 预期效果 由于业务的需要&#xff0c;很多情况下需要当满足某个条件时&#xff0c;某些查询条件才显示出来&#xff0c;如下图所示&#xff1a; 1.2 实现思路 由于业务的需要&#xff0c;很多情况下需要当满足某个…

[Flask]环境配置和项目框架的构建

一、配置flask 在Python环境下使用控制台的pip install命令进行安装 pip install Flask2.1.0 &#xff01;&#xff01;&#xff01;安装版本必须是2.x以上&#xff0c;否则可能出现错误&#xff1a;cannot import name escape from jinja2 &#xff08;flask 2.x放弃了对jinj…

CloudCompare 的简单的使用说明

File open&#xff1a;打开 save&#xff1a;保存 Global Shift settings&#xff1a;设置最大绝对坐标&#xff0c;最大实体对角线 Primitive Factory&#xff1a;对点云进行原始加工&#xff0c;改变原始点云的形状 3D mouse&#xff1a;对3D鼠标&#xff08;如3Dconnexion&a…

二叉树的绘制

目录 一、知乎方法 二、动手实践 DOT 语言 无向图 有向图 绘制二叉树 设置属性 如何绘制优美的二叉树 一、知乎方法 知乎上的大佬提供了一系列画图的方法&#xff0c;感兴趣的朋友可以自行去看看 用 Graphviz 绘制一棵漂亮的二叉树 - 南浦月 二叉搜索树&#xff0c…

庖丁解牛式讲解并发编程,GitHub高星最新开源阿里Java进阶必备手册

最近看到粉丝群里讨论最多的问题是现在Java这么卷&#xff0c;担心年龄大了以后会被裁员或者清退&#xff0c;要怎么做才能保住自己的“饭碗”。楼主认为不论是阅读优秀源码、领略设计思路&#xff0c;还是学习优秀架构、代码设计等等&#xff0c;都是加薪升职的正确路径&#…

安全分析开发环境 技术背景

安全分析开发环境一键部署 Gartner 报告中指出 [7]&#xff0c;Platfor m Ops for AI 是一种编排和扩展运营人工智能的方法&#xff0c;利用跨数据、机器学习、人工智能和应用程序 开发管道的多种最佳实践&#xff0c;为基于人工智能的系统创建一个高效的交付模型。Platform O…

RV1126笔记十七:吸烟行为检测及部署<四>

若该文为原创文章&#xff0c;转载请注明原文出处。 转换成onnx模型(windows) 一、查看pt文件 准备好训练好的pt文件,可以用Netron打开看看大概长啥样: 二、模型转换 主要的目的是想把训练好的pt文件转成onnx模型&#xff0c;为后面RV1126的部署做铺垫。 我们是在py38的con…

ChatGPT怎么突然变得这么强?华人博士万字长文深度拆解GPT-3.5能力起源

文章目录一、2020 版初代 GPT-3 与大规模预训练二、从 2020 版 GPT-3 到 2022 版 ChatGPT三、Code-Davinci-002和 Text-Davinci-002&#xff0c;在代码上训练&#xff0c;在指令上微调四、text-davinci-003 和 ChatGPT&#xff0c;基于人类反馈的强化学习的威力五、总结当前阶段…

SAP MM SPED输出报错-No authorization for delivery from shipping point US##-之对策

SAP MM SPED输出报错-No authorization for delivery from shipping point US##-之对策 前日收到某客户业务人员上报的一个问题&#xff0c;说是发现某个公司间STO单据的外向交货单里SPED 这个output不成功&#xff0c;不能成功触发inbound delivery单据。 相关报错信息如下&am…

项目实战之旅游网(四)后台角色管理(上)

目录 一.角色列表 二.新增角色 三.修改角色 四.删除角色 一.角色列表 接下来我们编写角色管理功能&#xff0c;首先查询角色列表: 1.编写RoleService Service public class RoleService {Autowiredprivate RoleMapper roleMapper;public Page<Role> findPage(int …

【无线通信】鲸鱼优化算法及其在无线网络资源分配中的应用(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

腾讯云Linux 轻量应用服务器如何搭建 FTP 服务?

Vsftpd&#xff08;very secure FTP daemon&#xff09;是众多 Linux 发行版中默认的 FTP 服务器。本文以 CentOS 7.6 64位操作系统的轻量应用服务器为例&#xff0c;使用 vsftpd 软件搭建 Linux 轻量应用服务器的 FTP 服务。 本文搭建 FTP 服务组成版本如下&#xff1a; Lin…

node.js+uni计算机毕设项目基于微信点餐系统小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

Allegro如何设置格点操作指导

Allegro如何设置格点操作指导 在PCB设计的时候,格点是一个很好用的工具,Allegro可以设置格点 具体操作如下 选择Setup-GirdsNon-Etch和All Etch中的X和Y都输入5,代表都是5的格点

CTF-MISC(入门|笔记|工具)

文章目录音频隐写直接隐写频谱隐写流量分析第一种流量包修复第二种是协议分析第三种数据提取图片分析zstegBMPPNGforemost 工具binwalksteghidestegslovetweakpngJPGF5-steganographystegdetect压缩包分析zip伪加密crc32zip包明文攻击其它条形码扫描pyc反编译数据转换到ASCII乱…

2022年安徽建筑施工电工(建筑特种作业)模拟试题及答案

百分百题库提供特种工&#xff08;电工&#xff09;考试试题、特种工&#xff08;电工&#xff09;考试真题、特种工&#xff08;电工&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 149.使⽤绝缘操作杆时&#xff0c;下列…

NTZ改机原理全公开(二)-- 实现篇

最核心的逻辑就是代码hook了,找这些hook点可不是一件容易的事情,但是借鉴这个事情就容易得多。这批文章就将这些hook列出来,帮大家节省时间。 主要的hook点为: 音频build属性连接上下文文件访问位置信息其他包管理传感器设置磁盘信息系统时钟系统属性电话相关wifi信息wifi…

uniapp微信小程序 引入腾讯视频插件

参考&#xff1a;微信小程序&#xff08;或uniapp&#xff09;引入腾讯视频插件播放视频_苏苏哇哈哈的博客-CSDN博客_微信小程序播放腾讯视频1.在manifest.json里面 "plugins": { "tencentvideo": { …

计算机图形学 着色(shader) 学习笔记

画家算法&#xff1a;从远到近的光栅化&#xff0c;近的物体的着色覆盖远的物体的着色&#xff0c;近的物体的RGB优先 实际计算机图形学使用的是Z-Buffer深度缓冲 先看地板&#xff0c;地板要渲染的像素先放进二位数组内&#xff0c;接下来要看正方体&#xff0c;正方体与之前…

前端路由应该理解的一些前置知识

1. URI和URL 与URI相比,我们更熟悉URL。URL正是使用web浏览器等访问web页面时需要输入的网页地址。比如我们经常输入的 https://www.baidu.com/ 就是 URL 1.1 统一资源标识符 URI是 Uniform Resource Identifier 的缩写。RFC2396分别对这三个单词进行了以下定义: Uniform …