【JavaEE】多线程代码案例(2)

在这里插入图片描述

🎏🎏🎏个人主页🎏🎏🎏
🎏🎏🎏JavaEE专栏🎏🎏🎏
🎏🎏🎏上一篇文章:多线程代码案例(1)🎏🎏🎏

文章目录

  • 1.线程池
    • 1.1概念
    • 1.2线程池如何提高效率
    • 1.3标准库线程池的参数分析
    • 1.4模拟实现一个线程池
      • 1.4.1一个固定数目的线程池
      • 1.4.2含有最大线程数的线程池
    • 1.5线程池优点
    • 2.定时器
    • 2.1概念
    • 2.2Java标准库中的定时器(Timer)
    • 2.3模拟实现定时器
      • 2.3.1定时器的需求
      • 2.3.2实现需求的技术
      • 2.3.3代码的实现
    • 1.4总结

1.线程池

1.1概念

将你需要用到的线程提前创建好,然后放到用户态通过数据结构的形式来管理。

1.2线程池如何提高效率

我们直接创建线程是内核态与用户态两一起配合完成的,如果频繁的去创建线程销毁线程,这样效率就会大大降低并且内核在完成一些任务是不可控的,面对这种情况我们就可以提前将我们需要用到的线程创建好放在用户态种通过数据结构管理起来,当需要使用的时候就通过用户态来调用,不要用就放回用户态中,减少内核态的参与相当于减少了不可控性那么效率就会提高。

1.3标准库线程池的参数分析

ThreadPoolExecutor
在这里插入图片描述

  1. int corePoolSize int maximumPoolSize
    corepoolSize(核心线程数)——根据CPU的逻辑核心数决定的
    maximumPoolSize(最大线程数)——由核心线程数+非核心线程数
    对线程池中的线程分为两种:核心线程和非核心线程
    核心线程:当线程池被创建了,核心线程也就有了。
    非核心线程:当任务过多的时候,核心线程处理不过来的时候,线程池就会临时创建线程来分担任务,当任务变轻的时候,那么这些非核心线程就会被回收,这样当任务繁重的时候,增加一些非核心线程就会提高效率,当任务空闲的时候就可以减少开销。
    那么最大的线程数应该设多少比较合适呢?
    关于设多少比较合适不仅仅和电脑的配置有关系还和代码类型有关系。
    代码类型在理想的情况下分为两种:CPU密集类型和IO密集类型
    CPU密集类型:
    代码中基本都是算术运算,条件判断,循环判断,函数调用这些都是需要大量调用CPU来参加工作的,那么这种最大的线程数应该要小于等于逻辑核心数。
    IO密集类型:
    IO类型的每一个线程消耗的CPU只有一点点,影响的主要是其他方面比如网卡带宽,硬盘访问…,那么最大线程数可以大于等于逻辑核心数。
    但我们生活中可不会有这种理想情况发生,一般都是CPU密集型于IO密集型结合的情况,那么这个最大的线程数怎么去确定呢,可以根据做实验的方式(控制变量法)来确定一个相对正确的数据。
  2. long keepAliveTime TimeUnit unit
    long keepAliveTime——允许非核心线程最大的空闲时间
    TimeUnit unit——空闲时间单位
    给定时间可以增加容错率,防止任务少的时候突然任务剧增,这样就可以给回收非核心线程缓冲时间。
  3. BlockingQueue workQueue
    BlockingQueue workQueue——线程池的任务队列
    线程池会提供submit方法,让其他线程将任务提交给线程池,此时的线程池就需要队列这样的数据结构,将任务管理起来,这个队列存储的元素其实就是Runnable对象,要执行的逻辑其实就是run方法中的内容
  4. ThreadFactory threadFactory
    ThreadFactory threadFactory——java标准库中提供的工厂类
    当我们需要创建一个点,有两种方式一种是笛卡尔坐标表示法,另一种是极坐标表示法
class Point {point(double x,double y) {}point(double r,double a) {}
}

此时的两种方式都是通过构造方法来创建的,但是这两种方法构成不了重载,所以无法实现,因为在某些特殊情况构造方法会带来一些麻烦,就出现了工厂方法来封装一些这些构造方法,这种模式叫工厂模式

class Point {public static point makePointByXY(double x, double y) {Point p = new Point();p.set(x);p.set(y);return p;}public static point makePointByRA(double r, double a) {Point p = new Point();p.set(r);p.set(a);return p;}
}

上述代码就是一种通过工厂方法来封装这些构造点的方法。
5. RejectedExecutionHandler handler
RejectedExecutionHandler handler——拒绝策略,是一种以枚举的方式表示的。

  1. AbortPolicy():超出负荷,直接抛出异常
  2. CallerRunsPolicy():调用者负责处理多出来的任务
  3. DiscardOldestPolicy():丢弃队列中最老的任务
  4. DiscardPolicy():丢弃新来的任务
    由于ThreadPoolExecutor用起来非常费劲,于是就提供了几个工厂类例如:Executor
    通过工厂类中提供的方法可以创建线程池的几种方式:
  5. Executors.newFixedThreadPool()——创建固定线程数目的线程池
  6. Executors.newSingleThreadExecutor()——创建一个只包含单个线程的线程池
  7. Executors.newScheduledThreadPool(4)——创建一个固定线程个数, 但是任务延时执行的线程池
    创建一个固定线程数的线程池:
public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(10);for (int i = 0; i <=50000 ; i++) {int id = i;service.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello " + id + ", " + Thread.currentThread().getName());}});}
}

1.4模拟实现一个线程池

  1. 需要若干个线程
  2. 需要任务队列
  3. 需要submit方法

1.4.1一个固定数目的线程池

//创建一个简单的线程
public class MyThreadPollBasicEdition {//创建一个任务队列public BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);//初始化线程池public MyThreadPollBasicEdition(int n) {for (int i = 0; i < n; i++) {Thread t = new Thread(()-> {try {while(true) {Runnable runnable = queue.take();runnable.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}}//提供submit方法public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}public static void main(String[] args) throws InterruptedException {MyThreadPollBasicEdition myThreadPollBasicEdition = new MyThreadPollBasicEdition(10);for (int i = 0; i < 50000; i++) {int id = i;myThreadPollBasicEdition.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello " + id + ", " + Thread.currentThread().getName());}});}}
}

1.4.2含有最大线程数的线程池

ic class MyThreadPollAdvancedEdition {//创建一个任务队列的对象public BlockingQueue<Runnable> queue = new ArrayBlockingQueue(1000);//创建一个最大线程数public  int maxThreadSize = 0;//创建一个集合来存储若干个线程public List<Thread> threadList = new ArrayList<>();//创建构造方法public MyThreadPollAdvancedEdition(int corePoolSize, int maxThreadSize) {this.maxThreadSize = maxThreadSize;for (int i = 0; i < corePoolSize; i++) {Thread t = new Thread(()->{try {while (true) {Runnable runnable = queue.take();runnable.run();}}catch (InterruptedException e) {e.printStackTrace();}});t.start();threadList.add(t);}}public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);//如果队列中的任务过多,导致线程不够用,可以增加一些线程if(queue.size() >=200 && threadList.size() < maxThreadSize) {Thread thread = new Thread(()-> {try {while(true) {Runnable runnable1 = queue.take();runnable1.run();}}catch (InterruptedException e) {e.printStackTrace();}});thread.start();threadList.add(thread);}}public static void main(String[] args) throws InterruptedException {MyThreadPollAdvancedEdition myThreadPollAdvancedEdition = new MyThreadPollAdvancedEdition(10,20);for (int i = 0; i < 50000; i++) {int id = i;myThreadPollAdvancedEdition.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello " + id + ", " + Thread.currentThread().getName());}});}}
}

1.5线程池优点

  1. 降低资源消耗:减少线程的创建和销毁带来的性能开销。
  2. 提高响应速度:当任务来时可以直接使用,不用等待线程创建
  3. 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。

2.定时器

2.1概念

用于实现定时操作、周期性任务和超时控制的作用

2.2Java标准库中的定时器(Timer)

Timer提供了一个schedule方法

public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello,2000");}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello,1000");}},1000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello,3000");}},3000);
}

2.3模拟实现定时器

2.3.1定时器的需求

  1. 能够延时执行任务和定时执行任务
  2. 能够管理多个任务

2.3.2实现需求的技术

  1. 需要一个描述任务和指定时间的类(本质就是一个Runnable)
  2. 需要一个数据结构来管理多个任务(优先级队列)
  3. 需要一个线程来扫描数据结构管理的任务

2.3.3代码的实现

//1.定义一个TimeTask类表示一个任务,这个类中需要任务执行的时间和描述任务。
class TimeTask implements Comparable<TimeTask> {public Runnable runnable;//此时的time不是程序等待的时间,而是一个绝对时间public long time;public TimeTask(Runnable runnable,long delay) {this.runnable = runnable;//手动换算时间,加一个时间戳将相对时间换算成绝对时间this.time = System.currentTimeMillis() + delay;}public void run() {runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(TimeTask o) {return (int) (this.time - o.time);}
}
//2.定义一个数据结构来管理多个任务,此处我们优先级队列来管理多个任务,因为用其他的数据结构去管理的话,
// 就需要不断去扫描数据结构中满足要求的成员,遇到数据量庞大的那么开销就巨大,得不偿失,
// 用优先级队列可以避免这种情况,由于要求等待时间短的先运行,那么我们可以定义一个小根堆。
class MyTime{Object locker = new Object();public PriorityQueue<TimeTask> queue = new PriorityQueue<>();//初始化线程,并且调用任务public MyTime() {//定义一个扫描线程来获取堆顶任务Thread t = new Thread(()-> {try {while(true) {synchronized (locker) {if (queue.size() == 0) {locker.wait();}//取小堆中堆顶的任务TimeTask task = queue.peek();//获取当前的时间戳long curTime = System.currentTimeMillis();//看任务的时间是否到了if(curTime >= task.getTime()) {//时间到了,则执行任务task.run();//任务执行完之后,则将队列中的堆顶任务消除queue.poll();} else {//任务时间没到,则堵塞,阻塞多久?堵塞任务等待的时间locker.wait(task.getTime() - curTime);}}}} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}//提供一个schedule方法来创建任务public void schedule(Runnable runnable,long delay) {synchronized (locker) {TimeTask timeTask = new TimeTask(runnable,delay);queue.offer(timeTask);//唤醒调用任务的线程locker.notify();}}
}
public class MyTimerTest {public static void main(String[] args) {MyTime myTime = new MyTime();myTime.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello,1000");}},1000);myTime.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello,2000");}},2000);myTime.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello,3000");}},3000);}
}

1.4总结

  1. 创建一个类,表示一个任务(Runnable 任务本体 time任务的执行时间)
  2. 引入数据结构来管理多个任务(用的是优先级队列,省去遍历的开销)
  3. 引入扫描线程,不停的循环获取队列队首任务,判定是否到时间,到时间就执行,并且出队列没到时间就阻塞。
  4. 引入锁,针对队列出和入的操作
  5. 解决忙等问题,引入wait和notify,队列为空wait(死等)队首任务没到时间wait(带有超时时间)这里不要用sleep(sleep通过interrupt唤醒是非常规手段,sleep不会释放锁,会影响后续插入任务)
  6. 引入比较规则,让TimeTask可以按照时间先后来制定优先级。

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

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

相关文章

用户资料门户的构建

1. 需求背景 老的页面停止维护了,且老旧, 功能单一,且页面分散. 急需做功能集成的平台化建设原先的用户资料查询没有做权限管控, 每一次查询都会消耗我们组的人力资源. 2. 项目介绍 2.1. 项目地址 服务地址: [公司内网服务(略)] 工蜂地址: [公司内网仓库(略)] 2.2 项目的价…

​​服务拆分的原则

目录 一、单一职责原则 二、服务自治原则 三、单向依赖 一、单一职责原则 单⼀职责原则原本是面向对象设计中的⼀个基本原则, 它指的是⼀个类应该专注于单⼀功能. 不要存在多于⼀个导致类变更的原因 在微服务架构中, ⼀个微服务也应该只负责⼀个功能或业务领域, 每个服务应该…

2024亚太赛(中文赛)数学建模竞赛选题建议+初步分析

提示&#xff1a;DS C君认为的难度&#xff1a;B<C<A&#xff0c;开放度&#xff1a;C<A<B。 综合评价来看 A题适合有较强计算几何和优化能力的团队&#xff0c;难度较高&#xff0c;但适用面较窄。 B题数据处理和分析为主&#xff0c;适合数据科学背景的团队…

【秋招突围】2024届秋招笔试-科大讯飞笔试题-04-三语言题解(Java/Cpp/Python)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系计划跟新各公司春秋招的笔试题 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f4e7; 清隆这边…

PDM系统中物料分类与编码规则生成方案

在企业管理软件中&#xff0c;PDM系统是企业管理的前端软件&#xff0c;用于管理研发图纸、BOM等数据&#xff0c;然后生成相关物料表或BOM&#xff0c;递交给后端ERP系统进行生产管理。在PDM系统中&#xff0c;有两种方式可以生成物料编码。 1第一种是用户可以通过软件接口将…

Linux学习第54天:Linux WIFI 驱动:蓝星互联

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 数字化、现代化的今天&#xff0c;随处的WIFI给与了大众极大的方便&#xff0c;也感受到了科技的力量。万物互联、无线互联越来越成为一个不可逆转的趋势。现在比较火…

Android EditText的属性与用法

EditText 是编辑框控件&#xff0c;可以接收用户输入&#xff0c;并在程序中对用户输入进行处理。EditText在App里随处可见&#xff0c;在进行搜索、聊天、拨号等需要输入信息的场合&#xff0c;都可以使用 EditText。 图1 编辑框示意图 EditText 是TextView的子类&#xff0c…

华为云盘古大模型5.0将如何促进工业领域的发展?

6月21日&#xff0c;在华为开发者大会2024&#xff08;HDC2024&#xff09;主题演讲环节中&#xff0c;华为常务董事、华为云CEO张平安重磅发布盘古大模型5.0&#xff0c;这一重要技术成果标志着人工智能在工业领域的应用迈出了新的步伐。当前&#xff0c;工业领域正面临着数字…

【ESP32】打造全网最强esp-idf基础教程——15.WiFi连接STA模式

WiFi连接STA模式 一、ESP32的WiFi功能介绍 前面章节内容&#xff0c;基本上都是描述了ESP32强大的MCU能力&#xff0c;这些MCU能力使得ESP32可以替换许多类型的单片机工作&#xff0c;而自己承担这部分功能&#xff1b;当然ESP32的IOT能力才是它的主业&#xff0c;从硬件配置来…

关于软件本地化,您应该了解什么?

软件本地化是调整软件应用程序以满足目标市场的语言、文化和技术要求的过程。它不仅仅涉及翻译用户界面&#xff1b;它包含一系列活动&#xff0c;以确保软件在目标语言环境中可用且相关。以下是您应该了解的有关软件本地化的一些关键方面&#xff1a; 了解范围 软件本地化是…

阳光倒灌试验太阳辐射系统日光模拟器

太阳光模拟器概述 太阳光模拟器是一种能在实验室环境下模拟太阳光照射特性的设备&#xff0c;广泛应用于材料科学、能源研究、环境科学等领域。通过模拟太阳光的光谱分布和辐射强度&#xff0c;太阳光模拟器可以为科研和工业提供稳定且可重复的光照条件&#xff0c;进而对材料…

人工智能|深度学习——多模态条件机制 Cross Attention 原理及实现

一、引入 虽然之前写过 Attention 的文章&#xff0c;但现在回头看之前写的一些文章&#xff0c;感觉都好啰嗦&#xff0c;正好下一篇要写的 Stable Diffusion 中有 cross-attention&#xff0c;索性就再单拎出来简单说一下 Attention 吧&#xff0c;那么这篇文章的作用有两个&…

46. 全排列

计算数组的全排列 给定一个不含重复数字的数组 nums&#xff0c;返回其所有可能的全排列。你可以按任意顺序返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例 2&#xff1a; 输入&a…

护网蓝队面试

一、sql注入分类 **原理&#xff1a;**没有对用户输入项进行验证和处理直接拼接到查询语句中 查询语句中插⼊恶意SQL代码传递后台sql服务器分析执行 **从注入参数类型分&#xff1a;**数字型注入、字符型注入 **从注入效果分&#xff1a;**报错注入、布尔注入、延时注入、联…

day04-组织架构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.组织架构-树组件应用树形组件-用层级结构展示信息&#xff0c;可展开或折叠。 2.组织架构-树组件自定义结构3.组织架构-获取组织架构数据4.组织架构-递归转化树形…

02-部署LVS-DR群集

1.LVS-DR工作原理 LVS-DR模式&#xff0c;Director Server作为群集的访问入口&#xff0c;不作为网购使用&#xff0c;节点Director Server 与 Real Server 需要在同一个网络中&#xff0c;返回给客户端的数据不需要经过Director Server 为了响应对整个群集的访问&#xff0c;…

Docker镜像加速配置

由于当前运营商网络问题&#xff0c;可能会导致您拉取 Docker Hub 镜像变慢&#xff0c;索引可以配置阿里云镜像加速器。阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 每个人镜像地址都不一样&#xff0c;需要登陆阿里云自行查看&#xff0c;地址在上面&a…

llama-factory训练RLHF-PPO模型

理论上RLHF&#xff08;强化学习&#xff09;效果比sft好&#xff0c;也更难训练。ppo有采用阶段,步骤比较多,训练速度很慢. 记录下工作中使用llama-factory调试rlhf-ppo算法流程及参数配置,希望对大家有所帮助. llama-factory版本: 0.8.2 一 rlhf流程 ppo训练流程图如下, 会…

油猴Safari浏览器插件:Tampermonkey for Mac 下载

Tampermonkey 是一个强大的浏览器扩展&#xff0c;用于运行用户脚本&#xff0c;这些脚本可以自定义和增强网页的功能。它允许用户在网页上执行各种自动化任务&#xff0c;比如自动填写表单、移除广告、改变页面布局等。适用浏览器&#xff1a; Tampermonkey 适用于多数主流浏览…

Golang | Leetcode Golang题解之第201题数字范围按位与

题目&#xff1a; 题解&#xff1a; func rangeBitwiseAnd(m int, n int) int {for m < n {n & (n - 1)}return n }