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

JavaEE初阶第十一期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(九)

专栏:JavaEE初阶起飞计划

个人主页:手握风云

目录

一、多线程案例

1.1. 线程池


一、多线程案例

1.1. 线程池

  • 概念

        线程的诞生,目的是为了解决进程“太重”的问题,但是如果创建销毁线程的频率太高,此时开销就无法优化了。

        线程池是管理和复用线程的组件,通过预创建线程并循环执行任务,避免频繁创建 / 销毁线程的开销。核心目标是优化资源利用、控制并发、提升性能。简单来说,就是将线程提前创建好,放到一个池子里,后续再申请线程,直接从池子里取,用完了之后,再放回到池子里。

        举个例子,当一个人出差时,遇到花钱的地方需要找公司报销。他可以碰到一个花钱的地方就走一遍报销流程,但显然效率太低。或者把所有花销的地方一起上报。

  • 标准库中的线程池

        我们这里重点学习第四种构造方法。线程池中的线程分为两种:1.核心线程;2.临时线程。核心线程和临时线程是根据线程的生命周期和作用划分的两类线程,主要用于平衡线程池的响应速度和资源消耗。线程池中常驻的线程,是线程池的 “正式员工”,数量由corePoolSize参数指定,属于一创建就申请的线程。线程池中为应对突发任务峰值而临时创建的线程,是 “临时工”,数量由maximumPoolSize - corePoolSize决定(即最大线程数与核心线程数的差值)。

        long keepAliveTime表示线程允许摸鱼的最大时间,TimeUnit unit表示时间单位,通过这两个单位来衡量临时线程的存活时间。

        BlockingQuece<Runnable> workQueue表示线程池要完成的任务队列,每个任务都是通过Runnable来描述的。线程池在使用的时候,首先会创建一些线程,然后需要调用者给线程池里面放一些任务,这些线程就会从队列中取出任务,并执行里面的代码。这本质上也是一个生产者消费者模型,线程属于消费者,执行的代码属于生产者。

        ThreadFactory threadFactory是工厂设计模式,是为了防止构造方法创建对象出现的缺陷。如下面的代码所示,下面的两个构造方法不符合方法重载的规定。那我们这里就可以不用构造方法进行初始化,直接通过静态方法进行初始化对象。

class Point {public Point(double x, double y) {// 笛卡尔坐标系表示}public Point(double r, double α) {// 极坐标系表示}
}
class Point {
}class PointBuilder {public static Point buildPoint_XY (double x, double y) {Point p = new Point();return p;}public static Point buildPoint_RΑ (double r, double α) {Point p = new Point();return p;}
}public class Demo1 {public static void main(String[] args) {Point p = PointBuilder.buildPoint_RΑ(10, 0);}
}

        而ThreadFactory就是Thread的工厂类,线程池中创建出来的线程就可以通过ThreadFactory进行初始化。ThreadFactory本质是一个接口,通过重写里面的方法来进行返回新的Thread类。

public interface ThreadFactory {/*** Constructs a new {@code Thread}.  Implementations may also initialize* priority, name, daemon status, {@code ThreadGroup}, etc.** @param r a runnable to be executed by new thread instance* @return constructed thread, or {@code null} if the request to*         create a thread is rejected*/Thread newThread(Runnable r);
}

        RejectedExecutionHandler handler,就是拒绝策略。线程池里面有任务队列,任务满了,如果再添加新的任务,会发生什么?正常情况下,我们写的代码是不希望出现这种突发性的阻塞,可能会对程序造成不可预估的影响。因此直接添加任务的阻塞队列是不太友好的,标准库中的线程池就引入了“拒绝策略”。

        AbortPolicy是线程池的默认拒绝策略。当任务被拒绝时,它会直接抛出 RejectedExecutionException异常。CallerRunsPolicy将任务回退到提交任务的线程(即调用execute()方法的线程)中执行。DiscardOldestPolicy会丢弃工作队列中最旧(等待时间最长)的任务,然后尝试将当前任务加入队列。DiscardPolicy是当任务被拒绝时,会直接丢弃当前尝试提交的任务,不做任何处理。

        由于上面的线程池参数比较多,使用起来也比较麻烦,标准库中也提供了简化版Executors

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Demo2 {public static void main(String[] args) {// 创建一个固定大小的线程池,大小为4ExecutorService service1 = Executors.newFixedThreadPool(4);ExecutorService service2 = Executors.newCachedThreadPool();// 创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程ExecutorService service3 = Executors.newSingleThreadExecutor();// 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行ExecutorService service4 = Executors.newScheduledThreadPool(10);// 创建一个定长线程池,支持定时及周期性任务执行for (int i = 0; i < 100; i++) {int id = i;service1.submit(new Runnable() {@Overridepublic void run() {System.out.println("执行一些任务:" + id);}});}}
}

  • 线程池的模拟实现
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;// 固定线程数目的线程池
class MyFixedThreadPool {private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();public MyFixedThreadPool(int n) {for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {while (true) {try {Runnable task = queue.take();task.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}}public void submit(Runnable task) throws InterruptedException {queue.put(task);}
}public class Demo3 {public static void main(String[] args) throws InterruptedException {MyFixedThreadPool threadPool = new MyFixedThreadPool(4);for (int i = 0; i < 100; i++) {int id = i;threadPool.submit(() -> {System.out.println("执行任务:" + id);});}}
}

http://www.lryc.cn/news/600714.html

相关文章:

  • 《计算机组成原理与汇编语言程序设计》实验报告二 基本数字逻辑及汉字显示
  • 告别配置混乱!Spring Boot 中 Properties 与 YAML 的深度解析与最佳实践
  • 非定长滑动窗口(持续更新)
  • Netty中AbstractChannelHandlerContext源码分析
  • C++连接MySQL完整教程
  • easy-llm-cli的安装和使用
  • Ubuntu安装node-red
  • 广东省省考备考(第五十七天7.26)——数量、言语(强化训练)
  • 【CTF-PWN】【攻防世界题目pwnstack】python攻击脚本ret(checksec、pwngdb、IDA)(含“/bin/sh“)
  • Traffic Lights set的使用
  • AI Agent开发学习系列 - langchain之LCEL(5):如何创建一个Agent?
  • Ansible列出常见操作系统的发行版,Ansible中使用facts变量的两种方式
  • 定义域第一题
  • InfluxDB Flux 查询协议实战应用(二)
  • 修改site-packages位置与pip配置
  • 网络:应用层
  • docker安装问题汇总
  • 一文速通《多元函数微分学》
  • AI Agent开发学习系列 - langchain之LCEL(4):Memory
  • x86汇编语言入门基础(三)汇编指令篇5 串操作
  • 【架构】Docker简单认知构建
  • JAVA学习-练习试用Java实现“深度优先搜索(DFS):实现八数码问题的解法(最短路径搜索)”
  • LangChain4j低阶+高阶Api+日志配置+监听器+重试机制+超时机制
  • 【LeetCode 热题 100】131. 分割回文串——回溯
  • 算法竞赛阶段二-数据结构(35)数据结构单链表模拟实现
  • Android-广播详解
  • golang实现一个定时引擎,功能包括按照corntab的时间任务实时增加、修改、删除定时任务
  • 常见sql深入优化( 二)
  • 一文学会c++list
  • 激光雷达-相机标定工具:支持普通相机和鱼眼相机的交互式标定