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控制浏览…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...