【Java并发】聊聊线程池原理以及实际应用

news2024/11/15 21:49:28

线程其实对于操作系统来说是宝贵的资源,java层面的线程其实本质还是依赖于操作系统内核的线程进行处理任务,如果频繁的创建、使用、销毁线程,那么势必会非常浪费资源以及性能不高,所以池化技术(数据库连接池、线程池)在性能优化的时候是重中之重。

我们来猜想以下线程池的功能,因为如果是一个线程一个线程执行任务,那么我们需要进行对线程的管理、以及对于任务的分配,在执行过程中,本质还是利用多个线程通过从任务队列中获取任务,进行执行。通过这种方式,当任务来临时可以直接使用线程,达到复用。

demo

	ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(15),
                new ThreadFactory() {
                    private final AtomicInteger atomicInteger = new AtomicInteger(1);

                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "pool-" + atomicInteger.getAndIncrement());
                    }
                }, new ThreadPoolExecutor.DiscardPolicy());

        //执行
        pool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("qxlxi");
            }
        });

        //关闭
        pool.shutdown();
        boolean terminated = false;
        while (!terminated) {
            pool.awaitTermination(100,TimeUnit.SECONDS);
        }
        System.out.println("pool is shutdowm.");

线程池的创建

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

corePoolSize :线程池中的常驻核心线程数 < core: >core : 缓冲队列,超过缓冲队列,就直接新建。核心线程不回销毁,而非核心线程超过一定时间没有使用,就会销毁。
maxmumPoolSize : 整个线程池的核心线程和非核心线程数, maxmumPoolSize - corePoolSize 就是非核心线程数。
keepAliveTime & unit : 非核心线程数销毁的时间,可以自定义
workQueue :当有新的任务请求线程时,超过核心线程数,那么就会将任务先存储到任务队列中,等待线程处理。是一个阻塞队列。
有Array、Linked、Priority、Synchron等。
handler : 当没有空闲线程进行处理任务的时候,超过来最大线程数,那么就需要执行线程池的拒绝策略。可以通过hanlder进行设置。只针对有届阻塞队列 可以看到通过一个抽象的接口,然后实现不同的策略来进行执行拒绝策略,当然我们也可以实现自己的策略拒绝类。

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

DiscardPolicy 什么也不做。

public static class DiscardPolicy implements RejectedExecutionHandler {

        public DiscardPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

AbortPolicy 直接返回异常。不执行

public static class AbortPolicy implements RejectedExecutionHandler {

        public AbortPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

CallerRunsPolicy 策略是 任务提交者来执行这个任务。

public static class CallerRunsPolicy implements RejectedExecutionHandler {

        public CallerRunsPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

DiscardOldestPolicy 策略是:判断线程是否关闭状态,没有关闭的化,直接删除workQueue中的一个任务,然后将其加入其中。

    public static class DiscardOldestPolicy implements RejectedExecutionHandler {

        public DiscardOldestPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

threadFactory
线程创建ThreadPoolExecutor对象时,传入ThreadFactory工厂类对象,那么线程池中的对象均会通过工厂类的new Thread()方法来实现。可以通过定义new Thread()对象来创建,添加一些信息。当然,我们还可以通过 newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor等方式。
在这里插入图片描述

线程池的执行

线程池执行任务的时候,只需要将执行的任务封装成Runnable对象,然后将Runnable对象传递给execute()函数,线程池在创建的时候,并不会提前创建,而是当有任务的时候才会创建。
1.核心线程是否已满,没有满 直接创建
2.核心线程已满,则检查等待队列是否已满,未满,将任务放入队列中
3.等待队列已满,检查非核心线程,非核心线程未满,常见非核心线程
4.核心线程、等待队列、非核心线程都满了,执行对应的拒绝策略
在这里插入图片描述
整体的流程,其实是创建核心线程之后,就会从wrokQueue中获取任务通过take()函数进行执行,如果没有任务的化就会阻塞等待,非核心线程创建之后,会调用workQueue()的poll(),不同从workQueue()获取任务,poll()函数是阻塞函数,根take()函数不同的时,poll()函数可以设置阻塞的超时时间,poll()的超时时间超过非核心线程的等待时间,那么就会超时返回,执行线程销毁。

关闭

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }
    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

线程池关闭有两个方法,一种是shutdown() 以及shutdownNow()。前者是通过优雅的方式,会执行完正在执行以及等待队列中的任务,后者则是通过直接将处理中,以及清空等待队列,并向所有线程发送中断请求。shutdownNow的返回值是等得队列中未被执行的任务。需要注意的是返回时,有可能线程池内还有线程在执行任务,需要等所有线程都执行完毕之后,调用awaitTermination函数阻塞等待。

配置

在实际的应用开发中,我们如何进行合理配置线程池的大小呢,一般通俗来说的化,IO密集型和计算密集型,计算密集型设置未CPU核心相当就可以,IO密集型因为大部分时间都在IO阻塞上,所以将线程池适当开大点。除此之外就是IO+计算相结合的方式。

具体的方式其实就需要统计花在IO和计算上的占比,pool_size * 核数 = (cpu_time + io_time) / cpu_time。
比如cpu_time 占用 1/3 io占用 2/3 那么就是3.
当然在实际的层面来说,还需要考虑别的地方有没有瓶颈,比如数据库连接池,文件句柄等。也就是木桶效应。根据最短的进行合理评估,在实际中,就遇到DB 连接池配置失效,当时吞吐量上不去,最后发现后才解决。

小结

参数说明:
corePoolSize:指定了线程池中的线程数量。
maximumPoolSize:指定了线程池中的最大线程数量。
keepAliveTime:当前线程池数量超过corePoolSize时,多余的空闲线程的存活时间,即多次时间内会被销毁。CachedThreadPool是60秒。
unit: keepAliveTime的单位。
workQueue:
blockQueue的配置。新加入任务的时候,当线程池中的可用线程小于第一个参数core线程数量,那么直接new一个线程或者用空闲的可用线程来执行任务。不用进行排队。当线程池中core线程数量都处于执行中,那么就把任务加入到blockqueue中进行排队等待。当排队队列满了,那么新new一个线程,执行最新加入到queue中的任务。如果线程池中的线程数量超过了最大线程数量,那么这个时候将拒绝新加入的任务。如果最大线程数都满了,队列中也满了,这个时候,还有新任务请求进来,那么会报错,默认是抛出RejectedException。

blockqueue有三种策略,
第一种是配置一个SynchronousQueue,这种queue其实不是真正的queue,他根本就不会进行排队,如果core线程数量满了,那么新来一个任务,会直接new一个线程,而不是进入排队!直到池中的线程数量超过最大线程数,开始拒绝新加入的任务,JDK的CacheThreadLocal就是使用的这个工作队列,配置的最大线程数是Integer.MAXVALUE。
第二种是配置一个LinkedBlockingQueue,这种queue本身是没有大小的,也就是说,这种queue永远也满不了,可以无限排,这个时候最大线程数就没有意义了,因为queue永远不满,所以,这种配置就相当于是一个固定的大小为core线程数的线程池,JDK的FixedThreadPool就是采用的这个
第三种策略是用ArrayBlockingQueue,我们可以给这个queue指定大小。比如200啊,300啊,那么,只要queue大小满了,就会产生新的线程来处理queue的头部任务。如果池中超过了最大线程数,那么会拒绝任务的加入。

threadFactory:线程工厂,用于创建线程,一般用默认的即可。如果默认的不能满足我们的要求,我们可以使用自定义的线程工厂。
RejectedExceptionHandler:拒绝策略,当BlockQueue都满了无法接收新的任务了,就会触发RejectedExceptionHandler的方法了,这是一个策略模式的很好的例子。JDK提供了几种现成的拒绝策略,默认的拒绝策略是AbortPolicy,抛出RejectedException,这是一个运行时的异常,但是线程池执行器依旧可以继续工作,再次提交新的任务的时候,可能又会抛出RejectedException。CallerRunsPolicy,这个策略是在调用者的线程中运行被抛弃的任务,相当于在线程池submit任务的时候,在调用者线程中执行runnable,显然,这个策略很糟糕;DiscardoldestPolicy,这个策略是将队列中最老的挤出去抛弃掉,然后再次提交该任务;DiscardPolicy,直接丢弃无法处理的任务,不做任何处理。一般来说,默认的拒绝策略是最好的,但是如果我们还想要自定义的拒绝策略,我们可以自己实现一个RejectedExceptionHandler策略。

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

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

相关文章

3D火山图绘制教程

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 本期教程内容 **注&#xff1a;**本教程详细内容 Volcano3D绘制3D火山图 一、前言 火山图是做差异分析中最常用到的图形&#xff0c;在前面的推文中&#xff0c;我们也推出了好几期火山图的绘制教程&#xff0…

Linux下载工具XDM下载安装与使用

Windows上IDM多线程下载非常强大&#xff0c;即能捕捉页面上的视频、图片、音频&#xff0c;又能作为浏览器下载器使用&#xff0c;但是IDM无法在Linux下使用&#xff0c;除非使用wine。不过我们可以在Linux中用XDM(Xtreme Download Manager)代替IDM。 1、XDM下载 Xtreme Dow…

从Discord的做法中学习 — 使用Golang进行请求合并

正如你可能之前看到的&#xff0c;Discord去年发布了一篇有价值的文章&#xff0c;讨论了他们成功存储了数万亿条消息。虽然有很多关于这篇文章的YouTube视频和文章&#xff0c;但我认为这篇文章中一个名为“数据服务为数据服务”的部分没有得到足够的关注。在这篇文章中&#…

如何在AD的PCB板做矩形槽孔以及如何倒圆弧角

Altium Designer 22下载安装教程-CSDN博客 如何在AD上创建完整的项目-CSDN博客 开始前&#xff0c;请先安装后AD&#xff0c;并创建好项目。 目录 1. 如何在AD的PCB板做矩形槽孔 2. 如何在AD的PCB板倒圆弧角 1. 如何在AD的PCB板做矩形槽孔 首先&#xff0c;我们进入上面创…

普通话考试相关(一文读懂)

文章目录&#xff1a; 一&#xff1a;相关常识 1.考试报名时间 2.报名地方 费用 证件 3.考试流程 4.普通话等级说明 二&#xff1a;题型 三&#xff1a;技巧 1.前三题 2.命题说话 四&#xff1a;普通话考试题库 1.在线题库 2.下载题库 一&#xff1a;相关常识 …

【工具栏】热部署不生效

目录 配置热部署&#xff1a; 解决热部署不生效&#xff1a; 首先检查&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 配置热部署&#xff1a; https://blog.csdn.net/m0_67930426/article/details/133690559 解决热部署不…

OmniGraffle

安装 在mac上安装OmniGraffle&#xff0c;找一个正版或者啥的都行&#xff0c;安装好后&#xff0c;可以直接在网上找一个激活码&#xff0c;然后找到软件的许可证&#xff0c;进行添加即可。 使用 新建空白页 然后图形啥的看一眼工具栏就知道了&#xff0c;颜色形状还是挺…

ELK企业级日志分析平台——ES集群监控

启用xpack认证 官网&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/7.6/configuring-tls.html#node-certificates 在elk1上生成证书 [rootelk1 ~]# cd /usr/share/elasticsearch/[rootelk1 elasticsearch]# bin/elasticsearch-certutil ca[rootelk1 ela…

九、ffmpeg命令转封装

开了几天小差&#xff0c;今天继续学习ffmpeg。 准备测试使用的视频&#xff0c;并查看其信息 # 查看视频信息。使用Mediainfo也可以 ffprobe test.mp4 视频格式的信息如下。 保持编码格式&#xff1a;ffmpeg -i test.mp4 -vcodec copy -acodec copy test_copy.tsffmpeg -i…

读书笔记——《黑猩猩的政治》

前言 弗朗斯德瓦尔&#xff08;Frans de Waal)的代表作《黑猩猩政治》成书于1982年&#xff0c;是它的首部书籍作品&#xff0c;也是美国国会新任议员的被推荐读物。之前看的他另一部作品的《万智有灵》是2016年的作品&#xff0c;时间跨度居然这么大。《万智有灵》介绍了许多…

6.2.SDP协议

那今天呢&#xff1f;我们来介绍一下sdp协议&#xff0c;那实际上呢&#xff1f;sdp协议非常的简单。我们如果拿到一个stp的文档去看的话&#xff0c;那你要分阅里边的所有的内容会觉得很枯燥&#xff0c;但实际上呢&#xff0c;如果我们按照这张图所展示的结构去看stp的话。你…

消息中间件——RabbitMQ(四)命令行与管控台的基本操作!

前言 在前面的文章中我们介绍过RabbitMQ的搭建&#xff1a;RabbitMQ的安装过以及各大主流消息中间件的对比&#xff1a;&#xff0c;本章就主要来介绍下我们之前安装的管控台是如何使用以及如何通过命令行进行操作。 1. 命令行操作 1.1 基础服务的命令操作 rabbitmqctl sto…

Linux快速显示文件行号并跳转

有时候&#xff0c;想要在线上直接查看日志文件&#xff0c;搜索到关键词后&#xff0c;如果一直按n找下去&#xff0c;很麻烦&#xff0c;我们可以先显示出行号&#xff0c;确定好我们要找内容对应的行号&#xff0c;直接跳转过去。 esc进入命令模式&#xff0c;输入:set nu命…

【神印王座】龙皓晨美妆胜过月夜,魔神皇识破无视,撮合月夜阿宝

Hello,小伙伴们&#xff0c;我是拾荒君。 《神印王座》国漫第82集已更新&#xff0c;拾荒君和大多数人一样&#xff0c;更新就去看了。魔神皇枫秀&#xff0c;威严凛然&#xff0c;突然空降月魔宫&#xff0c;整个宫殿都在这股无与伦比的强大气息中颤栗。为了顺利躲避魔神皇的…

筑牢思想防线——建行驻江门市分行纪检组举办2023年清廉合规大讲堂

为推动廉洁教育打通“最后一公里”&#xff0c;近日&#xff0c;建行驻江门市分行纪检组举办江门市分行2023年清廉合规大讲堂。 本次大讲堂检察官结合一线办案经历&#xff0c;从防范化解金融风险、预防金融从业人员犯罪等方面对全辖员工进行了深入浅出地的讲解&#xff0c;引导…

Volcano3D绘制3D火山图

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 本期教程内容 **注&#xff1a;**本教程详细内容 Volcano3D绘制3D火山图 一、前言 火山图是做差异分析中最常用到的图形&#xff0c;在前面的推文中&#xff0c;我们也推出了好几期火山图的绘制教程&#xff0…

如何下载OpenJDK及其源码

如果想下载 OpenJDK&#xff0c;存在以下几种办法&#xff1a; 最简单的办法是去 OpenJDK 官网&#xff0c;这里能下载 JDK9 及其以上的版本&#xff0c;还有 JDK 源码所在的 github 地址。 第二种方法是使用 IDEA 下载&#xff0c;位置在 File->Project Structure->SD…

Harmony 应用开发之size 脚本

作者&#xff1a;麦客奥德彪 在应用开发中&#xff0c;最终呈现在用户面前的UI&#xff0c;是用户能否继续使用应用的强力依据之一&#xff0c;在之前的开发中&#xff0c;Android 屏幕碎片化严重&#xff0c;所以出现了很多尺寸适配方案。 最小宽适配、百分比适配等等。 还有一…

单链表实现【队列】

目录 队列的概念及其结构 队列的实现 数组队列 链式队列 队列的常见接口的实现 主函数Test.c 头文件&函数声明Queue.h 头文件 函数声明 函数实现Queue.c 初始化QueueInit 创建节点Createnode 空间释放QueueDestroy 入队列QueuePush 出队列QueuePop 队头元…

【陈老板赠书活动 - 18期】-如何成为架构师这几本书推荐给你

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;赠书活动专栏&#xff08;为大家争取的福利&#xff0c;免费送书&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f468;‍&am…