多线程编程9——线程池

一、为什么要引入线程池?

虽然对比进程,线程已经很轻量了,创建销毁调度线程都更高效。但是随着并发程度的提高,我们对于性能要求的标准也越来越高,当我们需要频繁创建销毁调度线程时,就发现线程也没有那么轻量。于是就引入了线程池,来进一步提高效率。

二、线程池的理论知识

事先把需要的线程创建好,放到“线程池”中,后面使用的时候,直接从池里取;用完了,直接放回池中。从池中取和放回池中,这两个操作,比创建销毁线程更高效。

为什么呢?

因为从池中取和放回池中,是程序员自己的代码就可以实现的,想干啥,怎么干,都由程序员自己决定的,是用户态。

而创建和销毁线程,是由操作系统内核完成的,内核只会给我们提供一些API(也就是系统调用),我们只能够通过系统调用,让内核去完成创建或销毁线程,但我们并不清楚内核都要做哪些工作,身上背负着哪些任务,(内核并不是只给一个应用程序服务),什么时候才能帮我们去创建或销毁线程,整个过程是不可控的。

用户态比内核态更高效,所以使用线程池更高效。

三、线程池的使用

Java标准库中,提供了现成的线程池,可以直接使用。

这里使用到了“工厂模式”这种设计模式。

像 newFixedThreadPoll()这样的方法,称为“工厂方法”,提供这个工厂方法的类(Executors),称为“工厂类”。 工厂方法一般都是普通的静态方法,使用工厂类的工厂方法,来代替构造方法创建对象。(相当于把new对象的代码,放到工厂方法里了,我们只需要调用工厂方法,就能直接构造出一个对象来)

工厂类Executors 提供的线程池有很多种:

ExecutorService pool1 = Executors.newFixedThreadPool(10); 构造出固定线程数的线程池
ExecutorService pool2 = Executors.newCachedThreadPool(); 线程数量是动态变化的(如果任务多了,就多搞几个线程;如果任务少了,就少搞几个线程)
ExecutorService pool3 = Executors.newSingleThreadExecutor(); 线程池里只有一个线程
ScheduledExecutorService pool4 = Executors.newScheduledThreadPool(10); 类似于定时器,让任务延时执行。

我们用 Executors.newFixedThreadPool(10)来举例。

此处是通过工厂方法创建了一个10个线程的线程池对象(线程池里已经有10个线程了,这些线程都是前台线程),然后我们就可以随时安排这些线程去干活了。

通过线程池提供的 submit方法,可以给线程池提交若干个任务(往线程池的任务队列里放任务)

于是,线程池里的每个线程,就会自己从任务队列中取走一个任务去执行,执行完,再立即去取下一个任务去执行。

代码如下:

public static void main(String[] args) {
        //构造了一个10个线程的线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        //通过submit,往线程池的任务队列里放100个任务
        for (int i = 0; i < 100; i++) {
            int n = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    //往任务队列中放的的任务
                    System.out.println("hello "+n);
                }
            });
        }
    }

所以,使用Java线程的线程池时,只要通过submit往线程池的任务队列中指定任务就行啦,是不是非常方便呢。

四、实现一个线程池

实现固定数量线程的线程池

一个线程池里面至少要有2个大的部分

(1)阻塞队列:用来去保存任务

(2)若干个工作线程,每个线程的活:循环(从阻塞队列中取出任务,去执行)

提供一个submit方法,调用submit方法可以往任务队列中放任务。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyThreadPool{
    //任务队列
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    //线程数
    public MyThreadPool(int n){
        //创建n个线程,每个线程都会不停从任务队列里拿任务去执行
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(()->{
                while(true){
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }
    //往任务队列里放任务
    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}


public class ThreadDemo8 {
    public static void main(String[] args) {
        MyThreadPool myThreadPool = new MyThreadPool(10);
        for (int i = 0; i < 10; i++) {
            int n = i;
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello "+n);
                }
            });
        }
    }
}

五、认识  ThreadPoolExecutor类 中构造方法的参数

上面介绍的工厂类Executors提供的这些线程池,本质上都是通过包装 ThreadPoolExecutor来实现出来的。只是因为ThreadPoolExecutor这个线程池用起来有些麻烦,所以才提供了工厂类,让我们用起来比较简单。ThreadPoolExecutor用起来麻烦的原因主要是因为它提供的功能更强大。所以也需要我们对它的构造方法进行掌握。

ThreadPoolExecutor这个类也在 java.util.concurrent包底下,这个包里放的很多类都是和并发编程(多线程编程)密切相关的。

corePoolSize:核心线程数

maximumPoolSize:最大线程数

keepAliveTime 和 unit 描述了线程 可以闲着不干活 的最大时间

workQueue:线程池的任务队列

threadFactory:用于创建线程

handler:描述了线程池的“拒绝策略”

ThreadPoolExecutor 相当于把里面的线程分为2类:一类是公司的正式员工,一类是临时工。正式员工就相当于核心线程。

核心线程数就是正式员工的数目,最大线程数就是正式员工+临时工的数目

如果任务多,就可以多招一些临时工,多搞一些线程。但是一个程序的任务不一定始终都很多,任务少时,有些临时工就闲着了,就不需要这么多临时工了。就需要动态调节线程的数目,对现有的一些线程进行淘汰。

keepAliveTime 和 unit就描述了临时工 可以闲着不干活 的最大时间,unit是时间单位

workQueue,是线程池的任务队列,此处使用阻塞队列。如果队列为空,执行出队列操作就会阻塞,直到队列里又有活了(队列不为空)。如果队列满了,往队列里添加任务也会阻塞,阻塞到队列里的任务被线程拿走执行了(队列不满)

threadFactory,用于创建线程,线程池里是需要创建线程的

handler,描述了线程池的“拒绝策略”,是一个特殊的对象,描述了当线程任务队列满了时,如果继续添加任务会有啥样的行为。标准库提供了四个拒绝策略。

如果任务队列满了,第一种,直接抛异常;第二种 ,多出来的任务,谁加的,谁负责执行。第三种,丢弃最早的任务。第四种,丢弃最新的任务。

六、实际开发中,线程池的线程数,我们到底应该设置成多少合适呢?

不同的程序。特点不同,此时要设置的线程数也是不同的。

有两个极端情况:

第一种,CPU密集型。每个线程执行的任务都需要疯狂使用CPU(如:进行一系列算术运算),此时线程池的线程数,最多也不应该超过CPU核数。设置更多也没有,没有CPU给他用了。

第二种,IO密集型。每个线程要干的工作就是等待IO(如:读写硬盘,读写网卡,等待用户输入),此时这样的线程处于阻塞状态,不参与CPU的调度。这个时候你想搞多少线程都可以。

然而,这两种只是理想化的模型,真实的程序,往往一部分要用CPU,一部分要等待IO,具体几成工作量是用CPU,几成工作量是用IO,都是不确定的。所以线程池的线程数,应该设置成多少也是不确定的。

所以,确定线程数,一般通过测试/实验的方式去确定。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【基础算法】二分查找

1.二分查找 二分查找 思路&#xff1a; 朴素二分模版 class Solution { public:int search(vector<int>& nums, int target) {int l 0, r nums.size() - 1;while(l < r){int mid (l r) / 2;if(nums[mid] < target) l mid 1;else if(nums[mid] > ta…

综合性练习(后端代码练习1)——加法计算器

目录 一、准备工作 二、约定前后端交互接口 1、概念介绍 2、需求分析 3、接口定义 请求参数 响应数据 三、服务器代码 四、前端页面代码 五、运行测试 遇到问题如何解决&#xff1f; 需求&#xff1a;输入两个整数&#xff0c;点击 “点击相加” 按钮&#xff0c;显…

JAVA顺序表相关习题1

1.笔试题:cvte str1 :welcome to cvte str2:come 描述:删除第一个字符串当中出现的所有的第二个字符串的字符!结果:wlt vt 要求 用ArrayList完成! public class Test {public static List<Character> findSameWords(String u1, String u2){List<Character> listn…

前端请求没问题,后端正常运行,但查不出数据

写代码时写得快了些&#xff0c;Orders.的订单状态写错了CONFIRMED 改成COMPLETED

二、再识VUE-MVVM

一、初识VUE 二、再识VUE-MVVM 三、VUE数据代理 MVVM Vue.js 专注于 MVVM 模型的 ViewModel 层。它通过双向数据绑定把 View 层和 Model 层连接了起来。实际的 DOM 封装和输出格式都被抽象为了 Directives 和 Filters。 ViewModel 一个同步 Model 和 View 的对象。在 Vue.js…

汇川AM400PLC编码器转速测量功能块(M法测速)

M法测速的原理和相关代码,大家可以参考相关专栏文章,常用链接如下: 1、编码器M法测速仿真 编码器M法测速仿真(Simulink)_mt法测速 simulink-CSDN博客文章浏览阅读2k次。编码器M法和T法测速的详细讲解可以参看下面的文章链接,这里不再赘述,这里主要介绍Simulink里建模仿真…

(06)vite与ts的结合

文章目录 系列全集package.json在根目录创建 tsconfig.json 文件在根目录创建 vite.config.ts 文件index.html额外的类型声明 系列全集 &#xff08;01&#xff09;vite 从启动服务器开始 &#xff08;02&#xff09;vite环境变量配置 &#xff08;03&#xff09;vite 处理 c…

详细介绍如何使用YOLOv9 在医疗数据集上进行实例分割-含源码+数据集下载

深度学习彻底改变了医学图像分析。通过识别医学图像中的复杂模式,它可以帮助我们解释有关生物系统的重要见解。因此,如果您希望利用深度学习进行医疗诊断,本文可以成为在医疗数据集上微调YOLOv9 实例分割的良好起点。 实例分割模型不是简单地将区域分类为属于特定细胞类型,…

新质生产力实践,我用chatgpt开发网站

是的&#xff0c;我用chatgpt开发了一个网站&#xff0c;很轻松。 我之前一点不懂前端&#xff0c;也没有网站开发的代码基础&#xff0c;纯正的0基础。 从0开始到最后成品上线&#xff0c;时间总计起来大致一共花了2-3周的时间。 初始想法我是想给我公司开发一个网站&#…

3月8日是星期六

突然有查询特殊条件日期的需求。 <html> <title>3月8日是星期六</title> <center> <h1 id"h1"></h1> <div id"div"></div> </center> <script> var weekday [星期日, 星期一, 星期二, 星期…

Eclipse:-Dmaven.multiModuleProjectDirectory system propery is not set.

eclipse中使用maven插件的时候&#xff0c;运行run as maven build的时候报错 -Dmaven.multiModuleProjectDirectory system propery is not set. Check $M2_HOME environment variable and mvn script match. 可以设一个环境变量M2_HOME指向你的maven安装目录 M2_HOMED:\Apps\…

echarts开发技巧

tooltip 提示框组件相关的行为&#xff0c;必须引入提示框组件后才能使用。 tooltip: {trigger: axis,axisPointer: {type: cross,label: {backgroundColor: #6a7985,},},//为弹出层的value值增加百分号valueFormatter: function (value) {return value %}, }, tooltip.axi…

碳课堂|快速了解标准要点:ISO 14064-1

为了提高企业组织碳排放报告信誉度&#xff0c;国际标准化组织&#xff08;ISO&#xff09;发布了ISO14064 标准&#xff08;全称&#xff1a;《ISO 14064-1组织层次上对温室气体排放和清除的量化和报告的规范及指南》&#xff09;&#xff0c;报告中详细规定了公司温室气体清单…

确定性最大似然(DML)估计测角

1. 最大似然函数 贝叶斯方法是基于统计理论的一种经典方法&#xff0c;适合于有关参数估计问题。最大似然 (Maximum Likelihood&#xff0c;ML) 估计方法就是贝叶斯估计方法的一种特例&#xff0c;是在已知高斯噪声情况下的贝叶斯最优估计。在ML算法中&#xff0c;观测所得信号…

品牌出海新篇章:独立站构建与流量转化策略

在当今数字化时代&#xff0c;品牌出海已成为许多企业拓展国际市场的重要途径之一。在这个过程中&#xff0c;构建一个高效、专业的独立站&#xff0c;成为了品牌出海的重要一环。独立站不仅有助于企业塑造独特的品牌形象&#xff0c;更能通过精准的营销策略提高流量和转化率&a…

乘用车整车太阳光模拟加速老化试验太阳光模拟器

1.阳光模拟试验介绍 太阳辐射会对室外停放的汽车内外饰件产生热效应和光化学效应&#xff0c;影响汽车内外饰件的外观、性能&#xff0c;对汽车质产生不利影响。按照汽车产环境试验标准的要求&#xff0c;汽车在研制定型之前应进行太阳辐射试验&#xff0c;以考虑其对太阳辐射环…

微服务之分布式理论zookeeper概述

一、分布式技术相关的理论 CAP理论 CAP定理(CAP theorem)&#xff0c;⼜被称作布鲁尔定理(Eric Brewer)&#xff0c;1998年第⼀次提出. 最初提出是指分布式数据存储不可能同时提供以下三种保证中的两种以上: (1) ⼀致性(Consistency): 每次读取收到的信息都是最新的; (2) …

探索主播美颜工具与直播美颜SDK的技术奥秘

主播的形象美化是至关重要的一环&#xff0c;而实现这一目标的关键在于美颜工具和直播美颜SDK。接下来&#xff0c;我们将一同深入探索这些技术的奥秘&#xff0c;揭示它们背后的原理和工作方式。 一、美颜工具的背后 美颜工具是一类应用软件&#xff0c;旨在通过图像处理技术…

树莓派点亮LED灯

简介 使用GPIO Zero library 的 Python库实现点亮LED灯。接线 树莓派引脚参考图如下&#xff1a; LED正极 接GPIO17 LED负极 接GND 权限 将你的用户加到gpio组中&#xff0c; 否则无法控制GPIO sudo usermod -a -G gpio 代码 from gpiozero import LED from time impor…

基于H.264的RTP打包中的组合封包以及分片封包结构图简介及抓包分析;FU-A FU-B STAP-A STAP-B简介;

H.264视频流的RTP封装类型分析&#xff1a; 前言&#xff1a; 1.RTP打包原则&#xff1a; RTP的包长度必须要小于MTU(最大传输单元)&#xff0c;IP协议中MTU的最大长度为1500字节。除去IP报头&#xff08;20字节&#xff09;、UDP报头&#xff08;8字节&#xff09;、RTP头&a…