【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.mzph.cn/bicheng/40535.shtml

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

相关文章

女性经济崛起,天润融通用客户感知挖掘市场潜力

每逢一年一度的国际妇女节&#xff0c;“女性”话题都会被郑重地讨论。 从消费市场上来说&#xff0c;最近几年女性群体正在拥有越来越大的影响力&#xff0c;甚至出现了“她经济”这样的专属词汇在最近几年被市场反复讨论。 毫无疑问&#xff0c;女性消费群体的崛起已经成为…

监控平台—Zabbix对接grafana

目录 一、安装grafana并启动 二.浏览器访问 三、导入zabbix数据&#xff0c;对接grafana 四.如何导入模版 一、安装grafana并启动 添加一台服务器192.168.80.102 初始化操作 systemctl disable --now firewalld setenforce 0 vim /etc/selinux/config SELINUXdisabled cd /…

一文解开关于UWB定位技术的认识误区

作为一项新兴技术产业&#xff0c;UWB定位技术具有无限发展潜力。尤其是在TB行业应用中&#xff0c;UWB定位部分在项目的产值占比为10%-20%之间&#xff0c;这便意味着&#xff0c;UWB定位市场可以撬动其本身市场产值的5-10倍。 然而&#xff0c;伴随着UWB定位技术的迅速发展&a…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【通用密钥库基础概念】

通用密钥库基础概念 在使用通用密钥库完成应用开发前&#xff0c;开发者需要了解以下相关概念&#xff0c;以下概念将贯穿整个开发过程。 可信执行环境&#xff08;TEE&#xff09; 可信执行环境&#xff08;Trusted Execution Environment&#xff09;&#xff0c;简称TEE&…

论文解读StyleGAN系列——StyleGANv1

论文&#xff1a;A Style-Based Generator Architecture for Generative Adversarial Networks&#xff08;2018.12&#xff09; 作者&#xff1a;Tero Karras, Samuli Laine, Timo Aila 链接&#xff1a;https://arxiv.org/abs/1812.04948 代码&#xff1a;https://github.com…

Movable antenna 早期研究

原英文论文名字Historical Review of Fluid Antenna and Movable Antenna 最近&#xff0c;无线通信研究界对“流体天线”和“可移动天线”两种新兴天线技术的发展引起了极大的关注&#xff0c;这两种技术因其前所未有的灵活性和可重构性而极大地提高了无线应用中的系统性能。…

怎么把视频中走来走去的人去掉?

现在短视频火爆&#xff0c;很多朋友都会将生活中一些特定的场面拍摄记录下来。通过剪辑发布到一些短视频平台上&#xff0c;但是有时拍摄的视频不是那么完美&#xff0c;会拍到不相关的人或物&#xff0c;影响画面的主体&#xff0c;这种情况下我们可以去除视频中无关的走来走…

c++ primer plus 第15章友,异常和其他:友元类

c primer plus 第15章友&#xff0c;异常和其他&#xff1a;友元类 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;友元类 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的…

5个文章生成器免费版,自动写作文章更轻松

在这个信息如洪流般涌动的时代&#xff0c;写作所具有的重要性不言而喻。不管是学生需要完成的作业&#xff0c;还是职场人士得提交的报告&#xff0c;亦或是自媒体创作者必须输出的内容&#xff0c;都迫切要求我们具备一定的写作技能。然而&#xff0c;写作对很多人来说&#…

C++实现一个简单的Qt信号槽机制(1)

昨天写这个文章《深入探讨C的高级反射机制&#xff08;2&#xff09;&#xff1a;写个能用的反射库》的时候就在想&#xff0c;是不是也能在这套反射逻辑的基础上&#xff0c;实现一个类似Qt的信号槽机制&#xff1f; Qt信号槽机制简介 所谓的Qt的信号槽&#xff08;Signals …

隐私集合求交(PSI)原理深入浅出

隐私集合求交技术是多方安全计算领域的一个子问题&#xff0c;通常也被称为安全求交、隐私保护集合交集或者隐私交集技术等&#xff0c;其目的是允许持有各自数据集的双方或者多方&#xff0c;执行两方或者多方集合的交集计算&#xff0c;当PSI执行完成&#xff0c;一方或者两方…

@amap/amap-jsapi-loader实现高德地图嵌入React项目中,并且做到点击地图任意一处,获得它的经纬度

1.第一步要加入项目package.json中或者直接yarn install它都可以 想必大家应该都会 "amap/amap-jsapi-loader": "0.0.7"2.加入项目中 关于接口获取key的接口 大家改成自己对应的项目请求方法 import React, { PureComponent } from react; import { Input…

【前端--Vue】组件之间的多种通信方式,一文彻底搞懂组件通信!

本篇将重点讲解vue中的多种组件通信方式&#xff0c;包括【父传子】【子传父】【兄弟组件通信】【依赖注入】等等&#xff0c;并提供具体案例来让小伙伴们加深理解、彻底掌握&#xff01;喜欢的小伙伴们点赞收藏&#xff0c;持续关注哦~&#x1f495; &#x1f49f; 上一篇文章…

商务视频推广打造有吸引力的7个秘诀-华媒舍

商务视频推广是现代企业发展的重要工具&#xff0c;它能够帮助企业吸引更多的目标客户&#xff0c;提升品牌知名度&#xff0c;增加销售量。但是&#xff0c;如何打造一部有吸引力的商务视频推广呢&#xff1f;本文将为您介绍7个秘诀&#xff0c;帮助您在商务视频推广中取得成功…

性能测试-JMeter学习

1、给不同的访问口分配访问占比&#xff1b;例&#xff1a;登录30%&#xff0c;首页&#xff1a;20%&#xff0c;新增&#xff1a;50% 不同业务放到不同线程组里&#xff0c;实现不同业务的分配 使用吞吐量控制器&#xff0c;设置不同的占比 使用if控制器&#xff0c;设置不同…

单服务器推送还在用WebSocket?快试试更快的SSE

在传统的Web开发中&#xff0c;WebSocket常被用来实现实时双向通信。然而&#xff0c;对于只需要单向、从服务器到客户端的信息推送场景&#xff0c;Server-Sent Events (SSE) 提供了一种更轻量、更简单的解决方案。 SSE 和 WebSocket 特点的差异 SSE SSE 适用于服务器向客户…

恭喜!H医生一个月内荣获美国芝加哥大学访问学者邀请函

➡️【院校背景】 芝加哥大学&#xff08;英文&#xff1a;The University of Chicago&#xff0c;简称UChicago、“芝大”&#xff09;由石油大王约翰洛克菲勒于1890年创办&#xff0c;坐落于美国伊利诺伊州芝加哥市&#xff0c;一所私立研究型大学&#xff0c;属于全球大学校…

uboot run命令基本使用

run 命令可以用于运行环境变量的中定义的命令,run bootcmd 可以运行bootcmd中启动命令 作用:可以运行我们自定义的环境变量 include/command.h common/cli.c /*** board_run_command() - Fallback function to execute a command** When no command line features are enabled …

注意!高考志愿填报的两个优先原则,千万不要错过!

高考已经告一段落&#xff0c;接下来几天各省会陆续公布分数&#xff0c;然后就到了填报志愿的环节。高考志愿填报是一项影响深远的综合性决策&#xff0c;决定着每个考生的未来发展 。下面我谈谈我对高考填报的理解。我总结为&#xff1a;两个优先、三个因素。 一、两个优先 …

Prometheus 监控Kubelet的运行状态

kubelet通过/metrics暴露自身的指标数据。kubelet有两个端口都提供了这个url&#xff0c;一个是安全端口&#xff08;10250&#xff09;&#xff0c;一个是非安全端口&#xff08;10255&#xff0c;kubeadm安装的集群该端口是关闭的&#xff09;。安全端口使用https协议&#x…