当前位置: 首页 > article >正文

系列十二、线程池

一、线程池

1.1、为什么需要线程池

10年前单核CPU电脑,假的多线程,像马戏团小丑玩多个球,CPU需要来回切换。现在是多核电脑,多个线程各自跑在独立的CPU上,不用切换效率高。

1.2、优势

        线程池做的主要工作是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,那么超出数量的线程将排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

1.3、主要特点

线程复用、控制最大并发数、管理线程。

(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗;

(2)提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行;

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控;

1.4、架构说明

Java中的线程池是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类,如下所示:

1.5、常见的线程池

1.5.1、一池N线程

适用场景:执行长期任务性能好,创建一个线程池,一池有N个固定数量的线程。

说明:newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的是LinkedBlockingQueue

1.5.2、一池一线程

适用场景:一个任务一个任务的执行,一池一线程。

说明:newSingleThreadExecutor 创建的线程池corePoolSize和maximumPoolSize值都是1,它使用的是LinkedBlockingQueue

1.5.3、可扩容线程

适用场景:执行很多短期异步任务,线程池根据需要创建新线程,可扩容,遇强则强。

说明:newCachedThreadPool创建的线程池将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

1.6、 线程池案例

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/21 8:29
 * @Description: 一个银行网点有10个业务办理窗口,周末业务不繁忙开2个窗口,业务办理等待区最大能容纳20个人,模拟30个人去银行网点办理业务,使用线程池处理
 */
public class ThreadPoolMainApp {

    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(2,
                10,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(20),
                Executors.defaultThreadFactory(),
                // new ThreadPoolExecutor.AbortPolicy
                // new ThreadPoolExecutor.CallerRunsPolicy
                // new ThreadPoolExecutor.DiscardOldestPolicy
                new ThreadPoolExecutor.DiscardOldestPolicy());
        // 30个顾客请求
        try {
            for (int i = 1; i <= 30; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t办理业务!");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }

}

1.7、ThreadPoolExecutor的底层原理

 

1.8、线程池7大参数

1.9、线程池的底层工作原理

执行步骤:

        第一步:创建了线程池后,开始等待请求;

        第二步:当调用execute()方法添加一个请求任务时,线程池会做出如下判断:

                a、如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;

                b、如果正在运行的线程数量大于或者等于corePoolSize,那么将这个任务放入队列

                c、如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

                d、如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行

        第三步:当一个线程完成任务时,它会从队列中取下一个任务来执行;

        第四步:当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:

                a、如果当前运行的线程数大于corePoolSize,那么这个线程就会被线程池回收;

                b、线程池的所有任务完成后,它最终会收缩到corePoolSize的大小

1.10、线程池的拒绝策略

1.10.1、概述

等待队列已经排满了,再也塞不下新任务了,同时,线程池中的max线程也达到了,无法继续为新任务服务,这个是时候我们就需要拒绝策略机制合理的处理这个问题。

1.10.2、JDK内置的拒绝策略

AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行。

CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。

DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加人队列中尝试再次提交当前任务。

DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种策略。

说明:以上内置拒绝策略均实现了RejectedExecutionHandle接口

1.11、工作中单一的|固定数的|可变的三种创建线程池的方法哪个用的最多(超级大坑)

答:一个都不用,我们工作中只使用自定义的。

Executors中JDK已经给你提供了,为什么不用?

1.12、工作中如何使用线程池?是否自定义过线程池

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/21 8:29
 * @Description: 一个银行网点有10个业务办理窗口,周末业务不繁忙开2个窗口,业务办理等待区最大能容纳20个人,模拟30个人去银行网点办理业务,使用线程池处理
 */
public class ThreadPoolMainApp {

    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(2,
                10,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(20),
                Executors.defaultThreadFactory(),
                // new ThreadPoolExecutor.AbortPolicy
                // new ThreadPoolExecutor.CallerRunsPolicy
                // new ThreadPoolExecutor.DiscardOldestPolicy
                new ThreadPoolExecutor.DiscardOldestPolicy());
        // 30个顾客请求
        try {
            for (int i = 1; i <= 30; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t办理业务!");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }

}

 


http://www.kler.cn/news/136719.html

相关文章:

  • 【23CSPJ普及组】一元二次方程(uqe)
  • 【Vispy库】一个用于高性能交互式2D/3D数据可视化库 Python库
  • 什么是虚拟线程?Java 中虚拟线程的介绍与案例演示
  • 【LeetCode:910. 最小差值 II + 模拟 + 思维】
  • 电脑必备快捷键大全
  • PDF.js的使用及其跨域问题解决
  • 本地/笔记本/纯 cpu 部署、使用类 gpt 大模型
  • 企企通亮相广东智能装备产业发展大会:以数字化采购促进智能装备产业集群高质量发展
  • 05-Spring Boot工程中简化开发的方式Lombok和dev-tools
  • MQTT协议详解
  • LeetCode【76】最小覆盖子串
  • 中间件安全:Apache Tomcat 文件上传.(CVE-2017-12615)
  • Jmeter 吞吐量Per User作用
  • Android AIDL中使用Surface问题
  • 腾讯云服务器标准型S5和CVM标准型S6区别对比_选择攻略
  • pycharm安装教程
  • JavaScript-如何使用变量
  • elasticsearch 概述
  • 「Verilog学习笔记」输入序列连续的序列检测
  • Mistral 7B 比Llama 2更好的开源大模型 (四)
  • 身份证阅读器和社保卡读卡器Harmony鸿蒙系统ArkTS语言SDK开发包
  • PDF控件Spire.PDF for .NET【转换】演示:自定义宽度、高度将 PDF 转 SVG
  • ubuntu20编译安装pkg-config
  • [一周AI简讯]OpenAI宫斗;微软Bing Chat更名Copilot;Youtube测试音乐AI
  • 从哪些方面分析Linux内核源码
  • 如何实现MATLAB与Simulink的数据交互