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控制浏览…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
[USACO23FEB] Bakery S
题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC 的时间内生产一块饼干或在 t M t_M tM 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC,tM≤109)。由于空间…...
