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

Java---线程池讲解

什么是线程池

假设小明是一个女生,小明目前有一个男朋友(假设为A),但是过了一段时间,小明想换一个男朋友。
这时她就需要完成两步:
1:和A分手
2:和下一个男生(假设为B)培养感情
不容忽视的是,以上两步都需要消耗一定的时间和精力。
那我们有什么办法可以提高一下效率呢?
比如小明可以在和A进行分手的这段时间里,就已经在和B培养感情了,这样只要和A分手之后,B就立即可以成为小明的男朋友了。
也就是只要感情培养到位,此时只要分手完毕,此时下一个小哥哥就可以上位了。(下一个小哥哥此时就成为是“备胎”)
由于小明对换男朋友的需求比较高,一个备胎可能不够用,需要同时和多个备胎聊天,此时这多个备胎就构成了“备胎池”。

在Java中,线程池的作用就是为了让我们可以高效地创建和销毁线程的。

那么有的同学可能就会问了,为什么我们直接创建线程的开销要比从线程中取线程的开销要大呢?
这里我们就要引入操作系统中的内核态和用户态这两个概念了~~~

内核态和用户态

在一个操作系统之中,一份内核是要个所有的应用程序提供服务和支持的。

下面我们用小明去银行办理业务来详细说明一下内核态和用户态

假设银行的柜员要小明出示一下身份证复印件,此时小明并没有复印件。
小明可以选择两个解决办法
1:交给柜员,让柜员去复印
2:去大厅中的自己复印机那里自己去复印

在上述过程中,柜员就相当于是内核,而柜台前的各个用户就相当于是一个个应用程序

我们接下来对这两个过程做一个比较:
前者的过程相对来讲是不可控的,因为柜员在给小明复印身份证之前,也有可能在处理别的事情。
而我们自己去复印,这个过程和前者相比是可控的。

对应到计算机中:
如果一段代码是在应用程序中完成的,那么我们就认为整个执行过程是可控的。
与之对应的是,如果一段代码的执行需要进入到内核当中,由内核负责完成一系列工作,我们通常认为这个过程是不可控的。
因此我们通常认为,可控的过程要比不可控的过程更高效。

由于线程池很重要,所以Java标准库中也提供了我们可以直接使用的线程池。接下来我们就来详细讲解一下。

Java标准库中的线程池讲解

ThreadPooleExecuter当中就准备好了一些线程。

它其中的核心方法是submit(Runnable),我们通过Runnable描述一段我们要执行的任务,然后我们借用submit方法把任务放到线程池当中,此时线程池里的线程就会执行任务。

这个类的构造方法中最多可以有七个参数,接下来我们就来讲一下这七个参数分别是啥意思。

参数讲解

int corePoolsize:这表示核心线程数,即一个线程池一旦创建,这里面本身就包含一些线程,这些线程直到整个线程池销毁,它们才会销毁。

int maximumPoolsize:这表示最大线程数:这其中就包括了核心线程和非核心线程。
通过前面的学习我们知道,线程数量并不是越多越好。所以这些非核心线程在系统不繁忙的时候就销毁,系统繁忙的时候就再进行创建。

long keepAliveTime:这表示非核心线程允许空闲的最大时间。

Timeunit unit:上面的 keepAliveTime传入的是一个数值,但是具体是多长时间还并没有明确,这个类型的数据实际是一个枚举类型,比如秒,毫秒。
这两个参数加在一起就可以明确这个最大时间具体是多少了。
BlockingQueue<Runnable> workQueue
这就表示一个工作队列。
线程池本质上也是一个生产者消费者模型,我们调用submit就是在生产任务,而线程池里的线程就是在消费任务。生产和消费之间需要有一个队列来去传递数据,而这里的workQueue就起到了这样的作用。


ThreadFactory threadFactory
工厂模式就可以解决构造方法中的一些缺陷,我们先来看一下下面这段代码:


我们期望的是这两个构造方法可以构成“重载”,但是重载要求的是参数的个数或者参数的类型不同,为了解决这一缺陷,我们就引入了“工厂模式”。
下面我们着重来讲解一下工厂模式是如何解决这个缺陷的。

详解工厂模式

class Point{
}class Factory{public static Point makePointBYXY(double x,double y){Point P=new Point();
//        通过x和y对P进行属性设置return P;}public static Point makePointBYRA(double r,double a){Point P=new Point();
//        通过r和a对P进行属性设置return P;}
}public class Demo4 {public static void main(String[] args) {Point p=Factory.makePointBYRA(10,20);}
}

代码如上所示,这其中的makePointBYXY  和makePointBYRA就称为工厂方法,提供工厂方法的类就可以称之为“工厂类”。
工厂方法的核心就是通过静态方法,把构造对象new初始化的过程给封装起来,但是它可以提供多组静态方法,从而实现不同情况的构造。
这个Java标准库中提供的工厂模式就是为了便于我们对线程进行统一的构造和初始化。

RejectedExecutionHandler handler 拒绝策略

由于我们的submit是把要执行的任务添加到任务队列当中,而我们这里的任务队列是一个阻塞队列,当队列满的时候,再向其中添加元素,正常来讲会进行“阻塞等待”。
但在线程池当中,发现当入队操作无法执行(也就是队列已经满了的时候)并不会真的进行“阻塞等待”,而是会执行拒绝策略相关的代码。

具体一共有四种处理办法:下面我们来对这四种办法做一个较为详细的解释。ThreadPoolExecutor.AbortPolicy
这个处理办法会让线程池直接抛出异常(有可能会让线程池无法正常工作)
ThreadPoolExecutor.CallerRunsPolicy
这会让调用submit的线程自行执行任务
ThreadPoolExecutor.DiscardOldestPolicy
这会丢弃原先队列中最老的任务
ThreadPoolExecutor.DiscardPolicy
这会丢弃最新的任务,也就是当前submit的这个任务

Java标准库中也提供了另一组类,针对ThreadPoolExecutor进行了进一步地封装,用来简化线程池的使用。接下来我们就来讲一下Executors类。

Executors

这个类中提供了不同的静态方法,以便我们可以创建不同的线程
newFixedThreadPool方法创建的是固定线程数量的线程池,即核心线程数和最大线程数一致。
newCachedThreadPool方法所创建线程池其中的最大线程数是一个很大的数字。
下面我们用代码来演示一下。

public static void main(String[] args) {ExecutorService executorService =Executors.newFixedThreadPool(4);ExecutorService executorService2 =Executors.newCachedThreadPool();for (int i = 0; i < 1000; i++) {int id=i;executorService.submit(()->{System.out.println("hello "+ id +","+Thread.currentThread().getName());});}}

 

上述代码的运行结果如上,id的数值是乱序的原因如下:
上述循环过程是循环往线程池中去添加任务,这个线程池中共有4个线程,比如第一个线程拿到的id为0,但是当他还没有执行到输出语句的时候,系统就有可能调度执行其他的线程了。
简言之就是:线程之间是抢占式执行的 有可能执行到一半 就切换到其它线程执行了。

总结

其实线程池的核心思路特别简单:提前备好 线程,要用的时候直接拿,不用再重新创建,用完也不立马销毁,而是放回池里下次再用
这样既避免了频繁创建 / 销毁线程的高开销,又能高效处理大量任务。


文章转载自:

http://KFEZ1tY3.zfxrx.cn
http://QDBmL5bD.zfxrx.cn
http://sqMfgvcU.zfxrx.cn
http://YH8c8N4O.zfxrx.cn
http://zvBMijTf.zfxrx.cn
http://nb8OeNge.zfxrx.cn
http://S8TEr1ry.zfxrx.cn
http://yFhlDTSm.zfxrx.cn
http://ZNCL8HGv.zfxrx.cn
http://3frxuqH8.zfxrx.cn
http://0i1lg2Xq.zfxrx.cn
http://0MqMGzht.zfxrx.cn
http://DbJxYFrA.zfxrx.cn
http://a0UPZ32B.zfxrx.cn
http://aM0aHdkv.zfxrx.cn
http://7G0blFOJ.zfxrx.cn
http://luimWqQ7.zfxrx.cn
http://NnfQQ8bV.zfxrx.cn
http://CCeQ9RfQ.zfxrx.cn
http://IUD6uWvn.zfxrx.cn
http://XfryStc6.zfxrx.cn
http://YF0WPPep.zfxrx.cn
http://mOpywfeZ.zfxrx.cn
http://KJQub4qI.zfxrx.cn
http://x2uitNs3.zfxrx.cn
http://BCmejqGs.zfxrx.cn
http://yerCoRK7.zfxrx.cn
http://RpYtDytH.zfxrx.cn
http://yRT6RrNu.zfxrx.cn
http://0udSo2I9.zfxrx.cn
http://www.dtcms.com/a/385433.html

相关文章:

  • PEFT QLora Deepspeed Zero Stage 3 Offload Trainning
  • 线程概念,控制
  • 扫描仪常见样式:平板与馈纸的特性与适用场景
  • Python进程和线程——多线程
  • 2025年AIOCR审核革命!七大智能费控报销系统终结手工录入
  • 从循环到矩阵运算:矢量化加速机器学习的秘诀
  • R 语言入门实战|第七章 程序:从“老虎机”项目学透流程控制与代码优化
  • clickhouse 中SUM(CASE WHEN ...) 返回什么类型?
  • NR帧结构
  • 【联合查询】
  • 常见IC封装详解:从DIP到BGA的演进与应用
  • DockerComposeUI+cpolar:容器管理的远程可视化方案
  • tcp的三次握手与四次挥手简介
  • 2025算法八股——深度学习——MHA MQA GQA
  • 常见岩性分类与油气勘探意义笔记
  • 贪心算法应用:内存分配(First Fit)问题详解
  • RTK基站模块技术要点与作用解析
  • Istio与系统软中断:深度解析与问题排查全指南
  • 常用命令整理
  • PrestaShop 后台 Session 权限错误与产品链接 404 错误的解决指南
  • springboot“期待相遇”图书借阅系统的设计与实现(代码+数据库+LW)
  • SQLAlchemy -> Base.metadata.create_all(engine )详解
  • JVM 三色标记算法详解!
  • BUMP图改进凹凸贴图映射
  • 嵌入式硬件——I.MX6U-Mini 蜂鸣器(BEEP)模块
  • LeetCode 2799.统计完全子数组的数目
  • 蚂蚁T19 Hydro 158T矿机评测:强劲算力与高效冷却技术
  • Kafka架构:构建高吞吐量分布式消息系统的艺术——核心原理与实战编码解析
  • CCAFusion:用于红外与可见光图像融合的跨模态坐标注意力网络
  • 用 Python 玩转 Protocol Buffers(基于 edition=2023)