【JavaEE初阶】第十节.多线程 (基础篇 ) 线程池(案例四)

news2024/11/17 14:37:37

文章目录

前言

一、线程池概述

二、线程池的使用方式

2.1 Java标准库中 线程池的使用

2.2 自己动手来模拟实现一个线程池

总结



前言

本篇文章将介绍多线程案例 —— 线程池,这也是一个非常有用的案例

在之前已经介绍过,进程本身已经能做到并发编程,但是我们仍然创建了线程,是因为进程太重量了,创建和销毁的成本都比较高(需要申请释放资源)

线程,就是对上述问题的优化(共用同一组系统资源)

虽然如此,但是在更频繁创建释放的情况下,线程也不一定能够扛得住

因此,还需要去做进一步的优化;

 此时 可以有两种优化方式:

  1. 线程池
  2. 协程(又称 纤程,可以理解为 轻量级线程)

一、线程池概述

线程池和字符串常量池一样,都是为了提高程序的效率;

其解决问题的思路,就是把线程创建好了以后,放到池子里,当我们需要使用线程的时候,就可以直接从池子里取,而不是通过系统来创建;当线程用完了,也是还到池子里,而不是通过系统来销毁线程;

因此,上述操作 又能够进一步提高效率了

此时,就出现了一个关键问题:为什么把线程放到池子里,从池子里取线程就要比从系统这里创建线程 更高效呢?

原因是 从池子里去取 是纯用户态操作,通过系统来创建 涉及到内核态操作;

通常认为,牵扯到内核态的操作,就要比纯用户态的操作更低效!!!

内核态:操作系统内核执行的工作,如前面所介绍的 线程、进程、PCB 它们的一些相关的管理和调度,都是由 系统内核 来负责的;

当把任务交给内核态的时候,内核态不仅仅去完成交给它的工作,大概率还会伴随着其他的工作(内核态不会只为一个任务服务,还有其他的某些任务);而将工作交给用户态时,用户态仅仅完成交给它的工作;

所以说,内核态要做的事情多了(你交给它的任务的确可以干完,但是不确定是不是可以立即马上干你交给它的任务);但是,用户态可以立即马上 去执行你交给它的任务;

因此,用户态更为高效;

二、线程池的使用方式

2.1 Java标准库中 线程池的使用

这样就创建了一个线程池对象,创建了一个 固定线程个数的线程池(此处的固定数量是 10个);

 Executors 是一个类newFixedThreadPool() 是这个类的静态方法,可以借助这个静态方法 来创建实例,像这样的方法,称为 "工厂方法",对应的设计模式,就叫做 "工厂模式";

通常情况下,创建对象 是借助 new,调用构造方法 来实现的;

但是,C++ / Java 里面的构造方法,有诸多限制,在很多时候不方便使用;

因此就需要给构造方法 再包装一层,外面起到包装作用的方法 就是工厂方法;

构造方法的限制,在于 当前构造方法的名字 必须是和类名一样;

要想实现不同版本的构造,就需要重载构造方法,但是 重载构造方法 又要求参数类型和个数不同;


现在来列举一个关于 "工厂模式" 的例子:

//创建一个表示点的例子
class Point {
    //通过 横、纵坐标的方式
    public Point(double x,double y) {
      
    }
    //通过 极坐标的方式
    public Point(double r,double a) {
    }
}
//当直接构造对象的时候,就可以通过下面的来表示一个点:
Point p = Point.makePointByXY(30,60);

很明显,上面的代码不可以编译运行,无法构成重载,编译错误!!!

为了解决上述问题,就可以使用 工厂模式:

public static Point makePointByXY(double x,double y) {
    Ponit p = new Point();
    p.setX(x);
    p.setY(y);
    return p;
}
public static Point makePointByRA(double r,double a) {
    Point p = new Point();
    p.serR(r);
    p.setA(a);
    return p;
}

可以通过 上面的两种方法,来完成构造一个点的坐标;


当然,Executors还可以创建其他方式的线程池,这些不同的 "工厂方法" 其实是对 ThreadPoolExextor线程池原始类 的构造方法的包装(ThreadPoolExextor自身的构造方法太麻烦了,针对 ThreadPoolExextor这个类进行了 new,并且传入不同风格的参数,来达到构造不同种类线程池的目标):

其中,比较典型的两个线程池分别是: 

//固定个数的线程池
Executors.newFixedThreadPool(10);
//线程数量动态增加的线程池
Executors.newCachedThreadPool();

可以根据不同的需要,来选择不同的线程池:

package thread;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class Demo25 {
    public static void main(String[] args) {
        //创建线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        //安排任务
        //并且把任务加到线程池里面去,由线程池里面的线程负责 执行其中的任务~
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
    }
}

运行结果:

可以看见,程序一运行,"hello"就被打印出来了;

同时和定时器类似,线程池内部有一些线程阻止了程序的退出,所以需要手动退出;


线程池存在的目的,就是让程序员不必创建新的线程,直接使用已有的线程完成想要进行的工作即可;

注意;虽然上面的线程池只有 10 个线程,但是并不是说 线程只执行 10 个任务;

举例说明:

比如说,有一个餐馆里面需要洗客人吃饭过后 剩下的盘子;

服务员会把用过的盘子收起来,放到一个非常大的盆里,需要洗碗工来洗盘子;

虽然说,每个洗碗工每一次只能洗一个盘子,但是 这并不是说,每一个洗碗工只能洗一个盘子,他们洗完一个再洗下一个;

如果有 10个线程,给6个任务,那么也不一定10个线程都在工作(可能情况在 1~10 个之间);

举例说明:

10个洗碗工, 6个碗;

那么 可能1个洗碗工洗1个,有4个在摸鱼;可能有一个洗碗工洗两个 ;

线程之间的调度 是充满随机性的,但是 在更大的数据量级下,各个线程之间的工作是比较均衡的(相差的也不会太多)

 2.2 自己动手来模拟实现一个线程池

package thread;
 
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
 
//自己写的线程池类
//简单实现成,固定 10 个线程的线程池
class MyThreadPool {
    //核心操作,往线程池里插入任务
    //由于插入操作 一下就可以插入很多任务,那么就需要把当前尚未执行的任务都保存起来
    //使用阻塞队列来保存
    //这个队列就是 "任务队列",把当前线程池要完成的任务都放到这个队列中,
    //再由线程池内部的工作线程负责完成它们
    private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<> ();
    public void submit(Runnable runnable) {
        //提交任务
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public MyThreadPool(int n) {
        //n 设定线程池里面有几个线程
        //构造方法中,就需要创建一些线程,让这些线程负责完成上述执行任务的工作
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                //当前线程是否已经中断(中断:不执行 未中断:继续执行)
                while(!Thread.currentThread().isInterrupted()) {
                    try {
                        //把任务给取出来
                        Runnable runnable = queue.take();
                        //取出一个任务就执行一个任务
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break;
                    }
                }
            });
            t.start();
        }
    }
}
public class Demo26 {
    //小小测试一下
    public static void main(String[] args) {
        MyThreadPool myThreadPool = new MyThreadPool(10);
        for (int i = 0; i < 100; i++) {
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });
        }
    }
}
 

运行结果:

 


总结


以上就是今天要讲的内容,下一篇博客就会进入到 多线程的下一个案例的内容;

 

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

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

相关文章

HTTP错误响应码

4XX客户端错误 400 bad request 服务端认为客户端出现了错误&#xff0c;但是不能准确判断是哪种错误401 用户认证信息缺失或者不准确403 服务器理解请求&#xff0c;但是没有权限处理404 not found 服务器没有找到资源405 method not allowed 服务器不支持请求中的method方法…

VSCode加上dotnet7.0来创建开发,vs和vscode创建方法,命令创建和扩展方法创建区别,以及扩展推荐

吐槽一波&#xff1a; .net7出来第二天开始学习&#xff0c;前端vue3ts写法也得学 总监让熟悉一个星期新版本语言就动手开发&#xff0c;还要写前端&#xff0c;再带一个应届生开发&#xff0c;前端跑了我还得顶上&#xff0c; 天天催&#xff0c;天天问我进度怎么样&#xff0…

使用nginx给Nacos集群配置负载均衡

使用nginx的upstream模块进行负载第一、不想要加访问路径配置首先配置的时候需要把nacos配置的访问路径删除vim application.properties重新启动nacos 三个节点都配置一样的vim /etc/nginx/conf.d/default.conf 然后编写nginx的配置文件Nginx服务器ip 192.168.11.154upstream n…

SimCLS: A Simple Framework forContrastive Learning of Abstractive Summarization

论文 代码 摘要 本文提出了一个概念上简单但是强大的抽象摘要框架&#xff0c;SimCLS。它通过对比学习的方式&#xff0c;缓解了Seq2Seq框架固有的目标函数和评价指标不一致的问题&#xff0c;从而可以从模型生成的候选摘要中筛选出真实的评价指标(ROUGE)打分更高的摘要。将…

2023 中科院《国际期刊预警名单》发布,28 本学术期刊有风险,2区期刊14本,有哪些信息值得关注?

2023年1月31日&#xff0c;中国科学院文献情报中心终于发布了2023年《国际期刊预警名单&#xff08;试行&#xff09;》&#xff01; 名单分析&#xff1a; 与往年相比&#xff0c;今年的预警期刊名单变动还是相当大的 1. 生物学和数学期刊全部“洗白”&#xff0c;医学期刊…

Docker 安装可视化管理工具Portainer

简介Portainer是Docker 的图形化管理工具&#xff0c;提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作&#xff08;包括上传下载镜像&#xff0c;创建容器等操作&#xff09;、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理…

测试工程师笔试题整理01

来源&#xff1a; https://vt.quark.cn/blm/quark-doc-ssr-293/preview?idC2946438A278541B6C1284E5C22269AB 难度&#xff1a;⭐&#xff08;简单&#xff09; 一、基础题 1、对于软件测试描述正确的是 (目的是尽可能多的发现程序的错误)(2) 2、软件测试的对象包括(源程序…

解析Linux DMA mapping机制

说明&#xff1a; Kernel版本&#xff1a;4.14ARM64处理器&#xff0c;Contex-A53&#xff0c;双核使用工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 概述 DMA&#xff08;Direct Memory Access&#xff09;&#xff1a;直接存储器访问&#xff1b;先看问题的引入…

OpenWrt之IPTV双线融合教程

文章目录前言OpenWrt版本1. 让路由与光猫互通2. 设置Vlan3. 配置接口4. 配置路由表5. 播放测试后话前言 前段时间开始, 电信就开始整理单播源传播的乱象, 就限制了非内网无法打开直播源, 于是只能搞搞双线融合了, 让我的RTSP源重新焕发生机 OpenWrt版本 22.02 1. 让路由与光猫…

【docker概念和实践 5】(1) 容器数据卷

一、说明 本文的主要内容 什么是数据卷如何生成数据卷将数据卷挂在容器上多容器如何共享数据卷什么是绑定挂载二、数据卷概念 2.1 数据长久保存的要求 在容器运行时&#xff0c;容器内有一个数据存储空间&#xff0c;但是当容器关闭后&#xff0c;这个存储空间内容将被丢失&…

PYTHON官方教程:Python3.11中文版文档

Python 每年都会发布新版本&#xff0c;上半年是功能锁定的测试版&#xff0c;年底是最终版本。 Python 3.11 的特性集刚刚定稿&#xff0c;测试版本已经发布&#xff0c;开发人员在非生产代码上可以尝试使用这个最新版本&#xff0c;验证它能否在你的程序中工作&#xff0c;并…

小白入门pwn笔记--elf文件概述

笔记是根据哔哩哔哩视频所写2、ELF文件概述_哔哩哔哩_bilibili第一集看过大概&#xff0c;很久之前安装过一些工具&#xff0c;所以直接从第二集开始看&#xff0c;遇到没有安装的工具再安装。从今天开始入坑pwn0.pwn的简单介绍exploit&#xff1a;用于攻击的脚本与方案。paylo…

一、代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。

数组基础理论数组是存放在连续内存空间上的相同类型数据的集合。数组下标都是从0开始的。数组内存空间的地址是连续的正是因为数组的在内存空间的地址是连续的&#xff0c;所以我们在删除或者增添元素的时候&#xff0c;就难免要移动其他元素的地址。数组的元素是不能删的&…

关于CADC数据集的处理笔记

简要介绍 数据集 Canadian Adverse Driving Conditions Dataset&#xff08;CADC&#xff09;是全球首个针对寒冷环境的自动驾驶数据集&#xff0c;其内包含&#xff1a; 56,000 张相机图像&#xff1b;7,000 次 LiDAR 扫描&#xff1b;75 个场景&#xff0c;每个场景 50-100…

Vue中作用域插槽solt详解

作用域插槽插槽可以实现父子组件通信(通信的结构)默认插槽具名插槽作用域插槽:子组件的数据来源于父组件,子组件是决定不了自身结构与外观的直接上代码&#xff01;&#xff01;父组件&#xff1a;<template><div><h2>效果一: 显示TODO列表时, 已完成的TODO为…

蓝桥杯STM32G431RBT6学习——M24C02

蓝桥杯STM32G431RBT6学习——M24C02 前言 IIC是单片机的通用协议&#xff0c;在蓝桥杯单片机、嵌入式中都是考点。国信长天开发板板载M24C02&#xff08;IIC驱动&#xff09;作为调电存储模块&#xff0c;可以通过IIC对其写入数据后&#xff0c;掉电进行保存以供读取。其硬件…

Linux(centos7)基本操作---进程管理

进程管理进程管理&#xff08;process&#xff09;相关的名词解释静态进程查看动态进程查看进程控制作业管理&#xff08;jobs&#xff09;文件虚拟系统&#xff08;proc&#xff09;进程管理&#xff08;process&#xff09; 相关的名词解释 PID&#xff1a;进程的身份证号,…

对象之间的拷贝与转换

这里写目录标题Lambda 的 builderidea 自动生成插件 GenerateAllSetterMapStructDozer在开发的时候经常会有业务代码之间有很多的 JavaBean 之间的相互转化&#xff0c;比如 PO/DTO/VO/QueryParam 之间的转换问题&#xff0c;本文总结一下各种转换方法Lambda 的 builder 使用构…

【Qt】11.Linux下,Qt程序打包

目录 获取Release模式下的可执行文件 打包的目录结构搭建 安装linuxdeployqt 环境准备 下载linuxdeployqt 编译安装 复制快捷方式 配置环境变量 使生效 查看版本 安装unixODBC库 解压 配置 编译安装 重启系统 打包 原因 解决方法 打成deb包 打deb包目录构建…

Python虚拟环境大总结(virtualenv pipenv)

文章目录1 virtualenv1.1 安装1.2 测试安装是否成功1.3 主要命令1.4 virtualenvwrapper1.4.1 创建虚拟环境1.4.2 列举所有虚拟环境1.4.3 激活虚拟环境1.4.4 取消激活虚拟环境1.4.5 删除虚拟环境1.4.6 创建指定版本的虚拟环境2 pipenv2.1 安装2.2 创建虚拟环境2.3 进入虚拟环境2…