5.2 线程实际案例练习
文章目录
- 1.概述
- 2.实现方案一:继承Thread
- 2.1 代码实现
- 2.2 代码分析
- 3.实现方案二:实现Runnable接口
- 3.1 代码实现
- 3.2 代码分析
- 4.实现方案三:构建线程池
- 4.1 代码实现
- 4.2 代码分析
1.概述
接下来我们通过一个售票案例的实际操作来深入理解线程的相关应用;
需求:设计4个售票窗口,总计售票100张,使用多线程的方式进行售卖,当票售完后,停止售卖,在控制台打印具体为哪个线程售卖了第多少张票;
2.实现方案一:继承Thread
首先,我们通过简单的方法——继承Thread类来实现这一功能;
2.1 代码实现
package partFour;
/*设计多线程编程模型,4个窗口购机售票100张* 本方案使用多线程编程方案1,继承Thread类的方式来完成*/
public class TestThreat {public static void main(String[] args) {//5.创建多个线程对象TicketsThread t1 = new TicketsThread();TicketsThread t2= new TicketsThread();TicketsThread t3 = new TicketsThread();TicketsThread t4 = new TicketsThread();//6.以多线程的方式启动t1.start();t2.start();t3.start();t4.start();}
}
//1.自定义多线程售票类,继承Thread
class TicketsThread extends Thread{//3.定义变量,保存要卖的票数,需要设置静态,不然4个线程对象每个线程对象都会有100张票/*4个线程对象每个线程对象售票400张,原因是创建了4次对象,各自操作各自的成员变量* 解决:让所有对象共享同一个数据,票数需要设置为静态*/static int tickets = 100;//2.重新run方法@Overridepublic void run(){//4.1循环卖票,使用while循环,方便后续演示容易出错的位置while (true){//7.让每个线程经历休眠,增加线程状态切换频率与出错的概率//问题1:产生重卖的现象,同一张票卖给多人//问题2:产生了超卖的现象,超出了规定的票数100,出现了0,-1,-2try {Thread.sleep(10);//让当前线程休眠10ms} catch (InterruptedException e) {e.printStackTrace();}//4.2打印当前正在卖票的线程名称,并且票数-1System.out.println(getName()+"="+tickets--);//4.3做判断,如果没有票了,就退出死循环if (tickets<=0) break;//注意,死循环一定要设置出口}}
}
2.2 代码分析
- 每次创建线程对象,都会生成一个tickets变量值是100,创建4次对象就生成了400张票了。不符合需求,怎么解决呢?
能不能把tickets变量在每个对象间共享,就保证多少个对象都是卖这100张票。
解决方案: 用静态修饰 - 产生超卖,0 张 、-1张、-2张。
- 产生重卖,同一张票卖给多人。
- 多线程安全问题是如何出现的?常见情况是由于线程的随机性+访问延迟。
- 以后如何判断程序有没有线程安全问题?
在多线程程序中 + 有共享数据 + 多条语句操作共享数据就一定会有线程的并发安全问题,就一定考虑如何避免或解决这个问题;
在目前,我们只能考虑使用同步锁解决;
3.实现方案二:实现Runnable接口
接下来,我们将使用更复杂一点的Runnable接口来实现此功能;
3.1 代码实现
package partFour;
/*需求:设计多线程编程模型,4个窗口共计售票100张* 本方案使用多线程编程方案2,实现Runnable接口的方式来完成*/
public class TestRunnable {public static void main(String[] args) {//5.创建Runnable接口的实现类对象,作为目标业务对象TicketsRunnable target = new TicketsRunnable();//6.创建多个Thread类线程对象,并将target业务交给多个线程对象来处理Thread t1 = new Thread(target);Thread t2 = new Thread(target);Thread t3 = new Thread(target);Thread t4 = new Thread(target);//7.以多线程的方式启动多个线程对象t1.start();t2.start();t3.start();t4.start();}
}//1.自定义多线程类实现Runnable接口
class TicketsRunnable implements Runnable{//3.定义一个成员变量,用来保存票数100/*由于自定义类对象只创建了一次,所以票数被所有线程对象Thread共享* 不需要添加static,只卖了100张票,不会卖400次*/int tickets = 100;//2.添加接口中未实现的方法,方法里是我们的业务@Overridepublic void run(){//4.1循环卖票while (true){//8.让每个线程经历休眠,增加线程状态切换频率与出错的概率//问题1:产生重卖的现象,同一张票卖给多人//问题2:产生了超卖的现象,超出了规定的票数100,出现了0,-1,-2try {Thread.sleep(10);//让当前线程休眠10ms} catch (InterruptedException e) {e.printStackTrace();}//4.2打印当前正在售票的线程名称 & 票数-1System.out.println(Thread.currentThread().getName()+"="+tickets--);//4.3设置死循环的出口,没票了就停止卖票if(tickets<=0)break;}}
}
3.2 代码分析
- 实现Runnable接口中,因为只创建了一个对象,所以并不会生成多个tickets变量的值,所以此处我们不需要使用静态来修饰变量
- 同样产生超卖,0 张 、-1张、-2张。
- 同样产生重卖,同一张票卖给多人。
- 同样产生线程的并发安全问题;
4.实现方案三:构建线程池
如果在程序中创建大量的生命周期很短的线程,这会对性能产生比较大的影响,构建一个新的线程还算是一个比较大的开销;
此时,我们可以利用线程池很好的去解决这个问题,这样我们就不必将每个任务都映射到一个单独的线程上了。
线程池中会包含很多准备运行的线程,为线程池提供一个Runnable,就会有一个线程调用run方法,当run方法执行完毕后,线程并不会死亡,而是继续在池中等待下一个请求的调用。
我们通常使用Executors用来辅助创建线程池的工具类,常用的方法是:newFixedThreadPoo(int),这个方法可以帮我们创建指定数目线程的线程池;
4.1 代码实现
package partFour;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/* 本类用于回顾多线程售票案例2*/
public class TestRunnablevPool {public static void main(String[] args) {//1.创建实现类也就是目标业务对象TicktRunnable2 target = new TicktRunnable2();//2.使用Excutors创建最多包含5个线程的线程池--ExcutorService/*Executors是用来辅助创建线程池的工具类* 常用的方法是:newFixedThreadPoo(int)这个方法可以帮我们创建指定书目线程的线程池*/ExecutorService pool = Executors.newFixedThreadPool(5);//3.使用循环,启动线程for(int i=0; i<5; i++){/*execute让线程池中的线程来执行业务* 每次调用这个方法,都会将一个线程加到就绪队列中* 这个方法的参数,就是我们要执行的具体业务,也就是目标业务类对象target*/pool.execute(target);}}
}//1.创建接口实现类,作为目标业务量
class TicktRunnable2 implements Runnable{//3.1定义票数int tickets = 100;//8.注意在外部添加一个唯一的对象,Object o = new Object();//2.添加父接口中未实现的抽象方法,里面是业务@Overridepublic void run() {//3.2循环卖票while (true) {if (tickets>0)//7.为了增加线程状态切换概率与出错频率,在售票前休眠10毫秒try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口" + Thread.currentThread().getName() + "售票" + tickets--);if (tickets <= 0) break;}}
}
4.2 代码分析
- 通过构建线程池可以很好的解决资源浪费的问题
- 虽然使用了双重校验,但还是存在超卖,重卖的问题
相关文章:
5.2 线程实际案例练习
文章目录1.概述2.实现方案一:继承Thread2.1 代码实现2.2 代码分析3.实现方案二:实现Runnable接口3.1 代码实现3.2 代码分析4.实现方案三:构建线程池4.1 代码实现4.2 代码分析1.概述 接下来我们通过一个售票案例的实际操作来深入理解线程的相…...

stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
文章目录一、STM32串口常用寄存器和库函数1.1 常用的串口寄存器1.2 串口相关的库函数1.3 状态寄存器(USART_ SR)1.4 数据寄存器(USART_ DR)1.5 波特率寄存器(USART_BRR)二、串口配置一般步骤一、STM32串口常…...
山西省2023年软考报名3月14日开始
根据2023年上半年计算机技术与软件专业技术资格(水平)考试工作计划,可以得知,全国考务管理服务平台将于2023年3月13日开放,各地开始组织报名,如山西已发布2023上半年报名简章,从3月14号开始报名。 软考报名官网 大部…...

进程章节总结性实验
进程实验课笔记 本节需要有linux基础,懂基本的linux命令操作即可。 Ubuntu镜像下载 https://note.youdao.com/s/VxvU3eVC ubuntu安装 https://www.bilibili.com/video/BV1j44y1S7c2/?spm_id_from333.999.0.0 实验环境ubuntu22版本,那个linux环境都可以…...
【MyBatis】MyBatis的缓存
10、MyBatis的缓存 10.1、MyBatis的一级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问 使一级缓存失效的四种情况: 不…...

MyBatis基本使用
一、简介 MyBatis 中文文档 https://mybatis.org/mybatis-3/zh/index.html 1.什么是 MyBatis 概述:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBa…...

如何运行YOLOv6的代码实现目标识别?
YOLOv6是由美团视觉团队开发的1.环境配置我们先把YOLOv6的代码clone下来git clone https://github.com/meituan/YOLOv6.git安装一些必要的包pip install pycocotools2.0作者要求pytorch的版本是1.8.0,我的环境是1.7.0,也是可以正常运行的pip install -r requirement…...

新品BCM6755A1KFEBG/MT7921LE/MT7921AU WiFi芯片
博通在WiFi市场具有相当的实力。在WiFi6上有下面这几个解决方案:型号:BCM6755 BCM6755A1KFEBG类型:四核1.5GHz CPU封装:BGA批次:新BCM6755和BCM6750还是A7架构,更多的用在中低端型号上。BCM6755和BCM6750 C…...

析构函数、拷贝构造
1、析构函数析构函数的定义方式函数名和类名相同,在类名前加~,没有返回值类型,没有函数形参(不能重载)当对象生命周期结束的时候,系统会自动调用析构函数先调用析构函数,再释放对象的空间析构函…...
光学镜头是制作过程阶段理解
光学镜头是由多组镜片组合而成,它是摄影机投影一及显微镜上必不可少的部件。那么光学镜头是如何制造的呢?光学镜头的制作分为以下四个阶段:第一、首先将一大块光学玻璃用钻石锯片进行切片,然后用钻头在每一块玻璃切片上钻出多块冰…...

实验室设计|实验室设计要点SICOLAB
一、实验室设计规划要素1、实验室布局:实验室的布局要符合实验室工作流程,可以将实验室划分为干净区和污染区,以确保实验室的卫生和实验的准确性。2、设备选购:根据实验需要选择适当的设备,并确保设备的质量和性能符合…...

I.MX6ULL_Linux_系统篇(16) uboot分析-启动流程
原文链接:I.MX6ULL_系统篇(16) uboot分析-启动流程 – WSY Personal Blog (cpolar.cn) 前面我们详细的分析了 uboot 的顶层 Makefile,了解了 uboot 的编译流程。本章我们来详细的分析一下 uboot 的启动流程,理清 uboot 是如何启动的。通过对 …...

【C#】async关键字修饰后有无await的影响
文章目录测试总结拓展:js的async await问题参考测试 来自微软官网的说法: 异步方法通常包含 await 运算符的一个或多个匹配项,但缺少 await 表达式不会导致编译器错误。 如果异步方法未使用 await 运算符标记悬挂点,则该方法将作…...

Interspeech2022 | 一种基于元辅助学习的低资源口语语义理解方法
中国移动研究院首席科学家冯俊兰博士带领人工智能与智慧运营中心语音团队共同撰写的文章《Meta Auxiliary Learning for Low-resource Spoken Language Understanding》被语音国际顶会Interspeech2022接收。 关于Interspeech Interspeech 是国际最大且最全面关于言语科学与技…...

File类的用法和InputStream,OutputStream的用法
这里写自定义目录标题一、File类1.构造方法2.普通方法二、InputStream1.方法2.FileInputStream3.Scanner类的应用三、OutputStream1.方法2.FileOutputStream3.PrintWriter类的应用一、File类 1.构造方法 签名说明File(File parent, Stringchild)根据父目录 孩子文件路径&…...
Java多线程——Thread类的基本用法
一.线程的创建继承Thread类//继承Thread类class MyThread extends Thread{Overridepublic void run() {System.out.println("线程运行的代码");} } public class Demo1 {public static void main(String[] args) {MyThread t new MyThread();t.start();//启动线程&a…...

【C++】类和对象练习——日期类的实现
文章目录前言1. 日期的合法性判断2. 日期天数(/)2.1 和的重载2.2 对于两者复用的讨论3. 前置和后置重载4. 日期-天数(-/-)5. 前置- -和后置- -的重载6. 日期-日期7. 流插入<<重载8. 流提取>>重载9. 总结10. 源码展示前…...

[LeetCode周赛复盘] 第 333 场周赛20230219
[LeetCode周赛复盘] 第 333 场周赛20230219 一、本周周赛总结二、 [Easy] 6362. 合并两个二维数组 - 求和法1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6365. 将整数减少到零需要的最少操作数1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6364. 无平方子集计数1. 题目描…...

数字化时代,如何做好用户体验与应用性能管理
引言 随着数字化时代的到来,各个行业的应用系统从传统私有化部署逐渐转向公有云、行业云、微服务,这种变迁给运维部门和应用部门均带来了较大的挑战。基于当前企业 IT 运维均为多部门负责,且使用多种运维工具,因此,当…...

Python爬虫(7)selenium3种弹窗定位后点击操作,解决点击登录被隐藏iframe无法点击的登陆问题
之前的文章有关于更多操作方式详细解答,本篇基于前面的知识点进行操作,如果不了解可以先看之前的文章 Python爬虫(1)一次性搞定Selenium(新版)8种find_element元素定位方式 Python爬虫(2)-Selenium控制浏览…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...