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

【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可以按照时间先后来制定优先级。

相关文章:

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

&#x1f38f;&#x1f38f;&#x1f38f;个人主页&#x1f38f;&#x1f38f;&#x1f38f; &#x1f38f;&#x1f38f;&#x1f38f;JavaEE专栏&#x1f38f;&#x1f38f;&#x1f38f; &#x1f38f;&#x1f38f;&#x1f38f;上一篇文章&#xff1a;多线程代码案例(1)&a…...

Halcon支持向量机

一 支持向量机 1 支持向量机介绍&#xff1a; 支持向量机(Support Vector Machine&#xff0c;SVM)是Corinna Cortes和Vapnik于1995年首先提出的&#xff0c;它在解决小样本、非线性及高维模式识别表现出许多特有的优势。 2 支持向量机原理: 在n维空间中找到一个分类超平面…...

【Python机器学习】模型评估与改进——在模型选择中使用评估指标

我们通常希望&#xff0c;在使用GridSearchCV或cross_val_score进行模型选择时能够使用AUC等指标。scikit-learn提供了一种非常简单的实现方法&#xff0c;那就是scoring参数&#xff0c;它可以同时用于GridSearchCV和cross_val_score。你只需要提供一个字符串&#xff0c;用于…...

【C语言】union 关键字

在C语言中&#xff0c;union关键字用于定义联合体。联合体是一种特殊的数据结构&#xff0c;它允许不同的数据类型共享同一段内存。所有联合体成员共享同一个内存位置&#xff0c;因此联合体的大小取决于其最大成员的大小。 定义和使用联合体 基本定义 定义一个联合体类型时…...

电脑回收站删除的文件怎么恢复?5个恢复方法详解汇总!

电脑回收站删除的文件怎么恢复&#xff1f;在我们日常使用电脑的过程中&#xff0c;难免会遇到误删文件的情况。一旦发现自己误删文件了&#xff0c;先不要着急&#xff0c;还是有很多方法可以找回的。市面上还是有很多好用的文件恢复软件可以使用&#xff0c;具体介绍如下。 本…...

mac 安装cnpm 淘宝镜像记录

mac 安装cnpm 淘宝镜像记录 本文介绍了在安装cnpm时遇到权限问题的解决方案&#xff0c;包括使用sudo&#xff0c;处理SSL证书过期&#xff0c;以及因版本不一致导致的错误处理方法&#xff0c;步骤包括设置npm配置、卸载和重新安装cnpm到特定版本。 安装 npm install cnpm …...

ArcGIS Pro SDK (七)编辑 11 撤销重做

ArcGIS Pro SDK &#xff08;七&#xff09;编辑 11 撤销&重做 文章目录 ArcGIS Pro SDK &#xff08;七&#xff09;编辑 11 撤销&重做1 撤消/重做最近的操作 环境&#xff1a;Visual Studio 2022 .NET6 ArcGIS Pro SDK 3.0 1 撤消/重做最近的操作 //撤销 if (MapV…...

Excel 中的元素定位:相对定位、绝对定位和混合定位

在Excel中&#xff0c;单元格引用有三种主要类型&#xff1a;相对定位、绝对定位和混合定位。 这些类型主要用于公式和函数中&#xff0c;决定在复制或拖动公式时引用如何变化。 1. 相对定位 相对定位指的是不带“$”符号的单元格引用&#xff0c;例如 A1。 这种引用方式在…...

Idea2024安装后点击无响应

问题 最近因工作需要&#xff0c;升级一下 idea 版本&#xff0c;之前一直使用的是2020版本&#xff0c;下载最新的2024版本&#xff08;下载的 zip 包免安装模式&#xff0c;之前使用的2020版本也是免安装的&#xff0c;因为是免安装的&#xff0c;所以之前的版本也没有删除&…...

如何提高实验室分析结果的准确性呢

要提高实验室分析结果的准确性&#xff0c;可以从以下几个方面着手&#xff1a; 1、选择合适的实验方法 不同的实验方法具有不同的优缺点&#xff0c;实验方法的准确度直接影响测定结果的准确度。因此&#xff0c;在选择实验方法时&#xff0c;需要根据实验目的、实验原理、实…...

Perl 格式化输出:提升代码可读性的技巧

引言 Perl 是一种功能强大的脚本语言&#xff0c;广泛用于文本处理、系统管理、网络编程等多个领域。在 Perl 编程中&#xff0c;代码的格式化输出不仅有助于提升代码的可读性&#xff0c;还能增强程序的用户体验。本文将详细介绍如何在 Perl 中实现代码的格式化输出。 Perl …...

JavaScript基础-函数(完整版)

文章目录 函数基本使用函数提升函数参数arguments对象&#xff08;了解&#xff09;剩余参数(重点)展开运算符(...) 逻辑中断函数参数-默认参数函数返回值-return作用域(scope)全局作用域局部作用域变量的访问原则垃圾回收机制闭包 匿名函数函数表达式立即执行函数 箭头函数箭头…...

AI开发者的新选择:Mojo编程语言

随着人工智能技术的迅猛发展&#xff0c;编程语言的选择在AI项目的成功中扮演着至关重要的角色。近年来&#xff0c;Mojo编程语言作为一种专为AI开发者设计的新兴语言&#xff0c;逐渐引起了广泛关注。本文将详细介绍Mojo编程语言的特点、优势及其在AI开发中的应用。 目录 Mo…...

软考(高项)系统分析师--论软件开发模型及应用

文章目录 前言一、前期准备&#xff1a;二、论文部分: 前言 本文对系统分析师&#xff0c;软件开发模型及其应用文章进行展示&#xff0c;可以拷贝后直接粘贴到word 文档中。 一、前期准备&#xff1a; 项目主体功能项目背景常用的软件开发模型&#xff1a;瀑布模型&#xff…...

同一天提档又撤档!电影《野孩子》宣布取消7月10日公映安排——浔川电影报

同一天提档又撤档&#xff01; 7月3日晚上10点&#xff0c;电影野孩子 发声明官宣撤档&#xff0c;“由于后期进度原因&#xff0c;电影《野孩子》将取消7月10日的公映安排&#xff0c;我们向各影管院线的同仁及所有观众朋友们致以最诚挚的歉意&#xff0c;谢谢大家这段时间的…...

Shell编程之免交互

一、Here Document免交互 1&#xff1a;概述 Here Document 是一个特殊用途的代码块&#xff0c;它在 Linux Shell 中使用 I/O 重定向的方式将命令列表提供给交互式程序或命令&#xff0c;比如 ftp、cat 或 read 命令&#xff0c;Here Document 是标准输入的一种替代品 语法…...

基于opencv的斜光测距及python实现

1.前言 最近做了一个基于opencv的斜光测距的小项目&#xff0c;东西不多&#xff0c;但是很有意思&#xff0c;值得拿出来学一学。项目里面需要比较精确的定位功能&#xff0c;将前人matlab代码移植到python上&#xff0c;并且做了一些优化&#xff0c;简化逻辑(毕竟我是专业的…...

梯度下降算法

占楼&#xff0c;明天写...

第5章:软件工程

第5章&#xff1a;软件工程 软件工程概述 软件生命周期 软件过程 1.能力成熟度模型(CMM) CMM&#xff08;能力成熟度模型&#xff09;是一个评估和确定组织软件过程成熟度的模型。它最早于1987年由美国国防部软件工程研究所&#xff08;SEI&#xff09;提出&#xff0c;其目的…...

cefsharp在splitContainer.Panel2中显示调试工具DevTools(非弹出式)含源代码

一、弹出式调试工具 (ShowDevTools) ChromiumWebBrowser webbrowser; public void showDevTools(){//定位到某元素webbrowser.ShowDevTools(null, parameters.XCoord, parameters.YCoord);...

nginx部署多个项目;vue打包项目部署设置子路径访问;一个根域名(端口)配置多个子项目

本文解决&#xff1a; vue打包项目部署设置子路径访问&#xff1b;nginx部署多个子项目&#xff1b;一个ip/域名 端口 配置多个子项目&#xff1b;配置后&#xff0c;项目能访问&#xff0c;但是刷新页面就丢失的问题 注&#xff1a;本文需要nginx配置基础。基础不牢的可见文…...

02-部署LVS-DR群集

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

DataWhale-吃瓜教程学习笔记 (六)

学习视频**&#xff1a;第4章-决策树_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 第五章 5.1&#xff1b;5.2&#xff1b;5.3 文章目录 MP 神经元- 感知机模型 &#xff08;分类模型&#xff09;-- 损失函数定义--- 感知机学习算法 - 随机梯度下降法 - 神经网络需要解决的问…...

在docker配置Nginx环境配置

应用于商业模式集中&#xff0c;对于各种API的调用&#xff0c;对于我们想要的功能进行暴露&#xff0c;对于不用的进行拦截进行鉴权。用于后面的付费 开发环境 正式上线模式 一、常用命令 停止&#xff1a;docker stop Nginx重启&#xff1a;docker restart Nginx删除服务&a…...

在不修改.gitignore的情况下,忽略个人文件的提交

Git提供了一个assume-unchanged命令&#xff0c;可以将文件标记为“假设未更改”。这意味着Git将忽略该文件的更改&#xff0c;不会将其提交到仓库中。要使用该命令&#xff0c;只需运行以下命令&#xff1a; git update-index --assume-unchanged <file>其中&#xff0…...

【Unity navmeshaggent 组件】

【Unity navmeshaggent 组件】 组件概述&#xff1a; NavMeshAgent是Unity AI系统中的一个组件&#xff0c;它允许游戏对象&#xff08;通常是一个角色或AI&#xff09;在导航网格&#xff08;NavMesh&#xff09;上自动寻路。 组件属性&#xff1a; Radius&#xff1a;导航…...

51单片机第18步_将TIM0用作13位定时器

本章重点学习将TIM0用作13位定时器。 1、定时器0工作在模式0框图 2、定时器0工作在模式0举例 1、Keil C51中有一些关键字&#xff0c;需要牢记&#xff1a; interrupt 0&#xff1a;指定当前函数为外部中断0&#xff1b; interrupt 1&#xff1a;指定当前函数为定时器0中断…...

构建现代医疗:互联网医院系统源码与电子处方小程序开发教学

本篇文章&#xff0c;笔者将探讨互联网医院系统的源码结构和电子处方小程序的开发&#xff0c;帮助读者更好地理解和掌握这些前沿技术。 一、互联网医院系统源码结构 互联网医院系统通常由多个模块组成&#xff0c;每个模块负责不同的功能。以下是一个典型的互联网医院系统的主…...

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

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

10 - Python文件编程和异常

文件和异常 在实际开发中&#xff0c;常常需要对程序中的数据进行持久化操作&#xff0c;而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词&#xff0c;可能需要先科普一下关于文件系统的知识&#xff0c;对于这个概念&#xff0c;维基百科上给出…...