多线程(JavaEE初阶系列6)

目录

前言:

1.什么是线程池

2.标准库中的线程池

3.实现线程池

结束语:


前言:

在上一节中小编带着大家了解了一下Java标准库中的定时器的使用方式并给大家实现了一下,那么这节中小编将分享一下多线程中的线程池。给大家讲解一下什么是“池”,为什么要使用线程池。

1.什么是线程池

之前我们也有讲过“池”这个概念,我们讲过字符串常量池,数据连接池...

线程池就是提前把线程准备好,创建线程不是直接从系统中申请而是从池子中拿,当线程不用了的时候也是还给池子。它存在的目的就是为了提高效率,它最大的好处就是减少每次启动、销毁线程的损耗。线程的创建虽然比进程轻量,但是在频繁的创建的情况下,开销也是不可忽略的。

那么为什么从池子里拿比创建线程要更高效呢?

  • 从线程池拿线程,纯粹的用户态操作。
  • 从系统创建线程,涉及到用户态内核态之间的切换。真正的创建是要在内核态完成。

在上述过程中提到了用户态、内核态这两个概念,那么下来给大家解释一下什么是用户态,什么是内核态。

一个操作系统 = 内核 + 配套的应用程序

  • 其中内核就是操作系统中最核心的功能模块的集合,里面有硬件管理、各种驱动、进程管理、内存管理、文件系统...
  • 内核则需要给上层的应用程序提供支持。

比如我们执行:println("hello")这样的一个操作。

首先应用程序调用系统内核,告诉内核我要进行打印一个字符串的操作,内核再通过驱动程序,操作显示器来完成上述的请求。应用程序有很多,但是内核只有一个,内核要给这么多程序提供服务有的时候服务就不会那么及时。

举一个例子:比如银行里的工作人员和来银行办理事务的人,假设工作人员只有一个人,客户有很多,此时工作人员就相当于内核,客户就是用户。工作人员待的柜台就是用户态,银行的大厅就是用户态。如下图所示:

滑稽A此时来到柜台给工作人员说:我想办张银行卡。

此时就需要复印一些文件,这时我们有两种解决办法:

  1. A滑稽自己去大厅的复印机去复印,然后拿给工作人员。
  2. 工作人员去柜台里面的复印机去复印。

那么此时如果A滑稽自己去复印的话速度就会很快,立即复印,立即回来。但是如果是工作人员去复印的话可能就会很慢,因为柜台只有它一个人,他可能还需要给其他人提供服务。所以这也就例比于我们计算机中的用户态操作和内核态操作了,A滑稽自己复印就是用户态操作,而工作人员复印就是内核态操作。

结论:纯用户态操作,时间是可控的。涉及到内核态操作,时间就不太可控了。

2.标准库中的线程池

Java标准库中提供了线程的线程池。

ExecutorService pool = Executors.newFixedThreadPool(10);

 在这里我们可以看到和我们之前创建一个对象不太一样。

注意:这里我们使用的是工厂模式!!!

创建对象的时候不再直接new,而是使用一些其他方法(通常是静态方法)协助我们把对象创建出来。

Executors.newFixedThreadPoll()是不会设定固定值的,这里是按需创建,用完之后也不会立即销毁,留着以备后用。

下面我们来给大家演示一下:
代码展示:

package Time;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//线程池的使用
public class ThreadDemo3 {public static void main(String[] args) {//创建一个线程池ExecutorService pool = Executors.newFixedThreadPool(10);//添加任务到线程池中pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});}
}

结果展示:

说道这里我们就不得不去看一下Java的官方文档中对线程池的解释了。

我打开Java的官方文档,没有的同学可以点击这个链接进入☞Java Platform SE 8

进入之后点击下面的进入ThreadPoolExecutor的页面。

 我们可以看到它的参数有以下几个:

我们分别给大家解释一下:

  • corePollSize:核心线程数,如果类比一家公司的话,那么他们就是公司里面的正式员工。
  • maximumPollSize:最大线程数,相当于是公司中的正式员工+实习生。如果当前的任务比较多,线程池就会多创建一些“临时线程”。如果当前任务比较少,比较空闲了,线程池就会把多创建出来的临时线程给销毁。也就相当于在公司里面如果任务比较多的话就多招几个实习生,但是如果任务不多的话就辞退实习生,保留正式员工。
  • KeepAliveTime:线程的存活时间,如果任务不多了,那么临时创建的线程也不是一下子就销毁的,而是保留一段时间在销毁,相当于是在公司中,如果任务不多的话,实习生也不是瞬间就被辞退了,而是观察一段时间,看公司是不是长时间要处于一个不忙的状态。
  • unit:时间单位。
  • workQueue:线程池也要管理很多任务,这些任务也是通过阻塞队列来组织的,程序猿可以手动指定给线程池一个队列,此时程序猿就很方便的可以控制/获取队列中的信息了,submit方法其实就是把任务放到该队列中。
  • threadFactor:工程模式,创建线程的辅助的类。
  • handler: 线程池的拒绝策略,如果线程池的池子满了,继续往里添加任务,那么该如何进行拒绝呢?我们接着往下看。

关于线程池的拒绝策略,标准库给我们提供了四种。

下面来给大家分别解释一下:

  • ThreadPoolExecutor.AbortPolicy:表示如果任务满了,继续添加任务,添加的操作就会直接抛出异常。
  • ThreadPoolExecutor.CallerRunsPolicy:添加的线程自己负责执行这个任务。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃最老的任务。
  • ThreadPoolExecutor.DiscardPolicy:丢弃最新的任务。

3.实现线程池

下面我们就自己来实现一个线程池。

代码展示:

package Time;import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;//自己实现的线程池
class MyThreadPool{//阻塞队列用来存放任务private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}//此处实现一个固定线程数的线程池public MyThreadPool(int n) {for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {try {while (true) {//此处需要让线程池内部有一个while循环,不停的取任务Runnable runnable = queue.take();runnable.run();}} catch (InterruptedException e) {e.printStackTrace();}});t.start();}}
}
public class ThreadDemo4 {public static void main(String[] args) throws InterruptedException {MyThreadPool pool = new MyThreadPool(10);for (int i = 0; i < 1000; i++) {int number = i;pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello " + number);}});Thread.sleep(3000);}}
}


结果展示:

此处我们可以看到,线程池中任务的执行顺序和添加顺序不一定是相同的,这是非常正常的,因为这是个线程的调度是无序的。 

结束语:

这节小编就与大家分享到这里啦,这节中小编主要与大家分享了什么是线程池,带着大家一起看了标准库中的线程池,并且我们自己还实现了一遍,希望这节对大家线程池有一定了解,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)

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

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

相关文章

NoSQL-Redis集群

NoSQL-Redis集群 一、集群&#xff1a;1.单点Redis带来的问题&#xff1a;2.解决&#xff1a;3.集群的介绍&#xff1a;4.集群的优势&#xff1a;5.集群的实现方式&#xff1a; 二、集群的模式&#xff1a;1.类型&#xff1a;2.主从复制&#xff1a; 三、搭建主从复制&#xff…

在CentOS 7上挂载硬盘到系统的步骤及操作

目录 1&#xff1a;查询未挂载硬盘2&#xff1a;创建挂载目录3&#xff1a;检查磁盘是否被分区4&#xff1a;格式化硬盘5&#xff1a;挂载目录6&#xff1a;检查挂载状态7&#xff1a;设置开机自动挂载总结&#xff1a; 本文介绍了在CentOS 7上挂载硬盘到系统的详细步骤。通过确…

代码随想录算法训练营第二十八天 | Leetcode随机抽题检测

Leetcode随机抽题检测--使用题库&#xff1a;Leetcode热题100 1 两数之和未看解答自己编写的青春版重点题解的代码日后再次复习重新写 49 字母异位词分组未看解答自己编写的青春版重点题解的代码日后再次复习重新写 128 最长连续序列未看解答自己编写的青春版重点关于 left 和 …

C语言每日一题:12《数据结构》相交链表。

题目&#xff1a; 题目链接 思路一&#xff1a; 1.如果最后一个节点相同说明一定有交点。 2.使用两个循环获取一下长度&#xff0c;同时可以获取到尾节点。 3。注意初始化lenA和lenB为1&#xff0c;判断下一个节点是空是可以保留尾节点的。长度会少一个&#xff0c;尾节点没有…

根据前序和中序遍历序列构造二叉树 (递归+迭代两种方法实现)

给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]源代码如下…

【虚拟数字人】SadTalker简易部署教程

视频教程在这里&#xff1a; sadtalker数字人创建简易教程 项目基于SadTalkers实现视频唇形合成的Wav2lip。通过以视频文件方式进行语音驱动生成唇形&#xff0c;设置面部区域可配置的增强方式进行合成唇形&#xff08;人脸&#xff09;区域画面增强&#xff0c;提高生成唇形的…

修改conda环境缓存默认路径

前言&#xff1a;conda环境占用的内存太大了&#xff0c;每次建立一个新的虚拟环境都要加5个G差不多。所以想要修改默认的路径 问题1&#xff1a;找不到.condarc文件夹 创建condarc文件命令 conda config --add channels r 修改conda环境缓存默认路径 打开.condarc 添加 en…

xinput1_4.dll丢失怎么办?这几个方法都能解决

xinput1_4.dll是一个动态链接库文件&#xff08;DLL&#xff09;&#xff0c;它是Microsoft DirectX的一部分&#xff0c;用于处理游戏控制器输入。当你的电脑提示xinput1_4.dll文件丢失时&#xff0c;意味着与这个文件相关的游戏或应用程序无法正常运行。 当你的电脑提示xinp…

iPhone 8 Plus透明屏应用范围详解

iPhone 8 Plus是苹果公司于2017年推出的一款智能手机&#xff0c;它采用了全新的玻璃机身设计&#xff0c;支持无线充电&#xff0c;并且搭载了更强大的A11仿生芯片。 而透明屏则是一种新型的屏幕技术&#xff0c;可以使手机屏幕呈现出透明的效果。 透明屏是一种将屏幕背后的元…

基于深度学习的裂纹图像分类研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

11、springboot项目启动时对容器中的bean进行延迟初始化

springboot项目启动时对容器中的bean进行延迟初始化 预初始化&#xff1a; Spring Boot在启动应用时&#xff0c;会启动Spring容器&#xff0c;当启动Spring容器时&#xff0c;Spring会自动初始化容器中所有的singleton Bean——这是默认行为 预初始化的好处&#xff1a; 1、项…

MATLAB编程实践12、13

生命游戏 游戏的宇宙是无限可扩展的二维矩形网格&#xff0c;群体是那些标注为存活的网格的集合。群体可以依照称为代的离散时间步距进化。在每一步中&#xff0c;每个网格的命运由它周围最近的8个网格邻居的活度决定&#xff0c;规则如下&#xff1a; 如果一个存活的网格有两个…

dubbo-helloworld示例

1、工程架构 2、创建模块 &#xff08;1&#xff09;创建父工程,引入公共依赖 pom.xml依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></depende…

图像处理库(Opencv, Matplotlib, PIL)以及三者之间的转换

文章目录 1. Opencv2. Matplotlib3. PIL4. 三者的区别和相互转换5. Torchvision 中的相关转换库5.1 ToPILImage([mode])5.2 ToTensor5.3 PILToTensor 1. Opencv opencv的基本图像类型可以和numpy数组相互转化&#xff0c;因此可以直接调用torch.from_numpy(img) 将图像转换成t…

SQL-事务

set autocommit 0; select * from acount where name 嘉宝 && acount.money > 1000; update acount set money money - 1000 where name 嘉宝; update acount set money money 1000 where name 煎包; commit ; 脏读;当有两个事务使用同一数据库时&#xff0c…

SQL SERVER使用发布订阅同步数据库遇到的坑

可能遇到的各种坑 1.在执行 xp_cmdshell 的过程中出错。调用 ‘CreateProcess’ 失败&#xff0c;错误代码: ‘5’ 网上有各种解决办法&#xff0c;包括改本地安全策略&#xff0c;将sql server服务的网络权限改为本机系统&#xff0c;改cmd用户的读写权限&#xff0c;退出360…

基于WSL2、Ubuntu和VS Code的CUDA平台运行C语言程序

一、CUDA程序执行方法 执行步骤为&#xff1a; 安装Visual Studio Code。在Visual Studio Code中安装插件WSL与电脑的WSL2进行连接。点击左下角&#xff0c;然后再选择连接到WSL。 在WSL中创建以 .cu 为后缀的文件。 rootDESKTOP-HR6VO5J:~# mkdir CUDA /…

【NLP-新工具】语音转文本与OpenAI的用途

一、说明 OpenAI最近2022发布了一个名为Whisper的新语音识别模型。与DALLE-2和GPT-3不同&#xff0c;Whisper是一个免费的开源模型。它的主要功能就是将语音翻译成文本。本文将介绍如何使用这个重要应用库。 二、 Whisper概念 2.1 Whisper是啥&#xff1f; Whisper 是一种自动…

Linux实战:五子棋

一、五子棋原理 采用二维数组保存棋盘信息,棋盘上面的任何一个位置,里面可以放置三类信息。 空用户1的落子(黑子)用户2的落子(白子)下棋就是在二维数组中找对应的空位置,进行落子落完子之后下来就要考虑该落子位置是否有”五子连珠“,进而进行输赢判定,每一次走棋,多…

selenium如何打开浏览器,等待用户输入完成后,再运行

selenium如何打开浏览器&#xff0c;等待用户输入完成后&#xff0c;再运行 一、在脚本中&#xff0c;等待用户输入 在使用 Selenium 打开浏览器后等待用户输入完成&#xff0c;可以使用 Python 编写一个简单的脚本来实现。首先&#xff0c;确保你已经安装了 Selenium 和对应的…