Java线程同步-模拟买票
文章首发于 2020-11-29
知乎文章:Java线程同步-模拟买票
作者:落雨湿红尘(也是我o)
01 导语
本文使用JAVA代码模拟买票场景下的业务交互,通过示例讲解线程的初始化、线程同步等java线程基础知识。
提出这样一个问题:三个人排队买电影票(每张5元)。三个人按前后顺序分别为张某(拿着20元钞票)、李某(拿着10元钞票)和赵某(拿着5元钞票)。而售票员的钱箱中现在只有3张5元钞票,试对这样一个场景进行模拟
02 线程同步问题
当多个线程对共享资源进行操作时,势必会出现进程间相互争夺资源的情况,而线程同步问题则是规划各线程间操作顺序,让每个线程不受其他线程打扰的情况下独自安全的操作共享资源。
而在本场景中,那个售票员就是这样一个共享资源。在对每个人受理业务(即每个线程)的过程中,这个售票员钱箱里的钱会发生变化,但是每个人受理业务时,不能被其他线程打扰(即别人不能插队)
下面构造售票员这样一个共享资源,其有一个找零功能change()。
找零算法的思想是:对于需要找零的金额moneyChange,若其等于票价,就不需要找零。否则,用售票元手上的每个币种,从大到小依次找零。每找零一次,找零金额moneyChange就会减小。最后当最小币种找零时,若其不足以找零,说明售票员手上的钱是不足以找零的,返回一个false。若其可以找零,说明售票员手上的钱足以找零,把受理对象钱收下,给他票,返回true。
//售票员
class Seller { public static final int PRICE = 5;//票单价5元private int five ;//5元纸币数量private int ten ;//10元纸币数量private int twenty ;//20元纸币数量//getter and setter public int getFive() {return five;}public void setFive(int five) {this.five = five;}public int getTen() {return ten;}public void setTen(int ten) {this.ten = ten;}public int getTwenty() {return twenty;}public void setTwenty(int twenty) {this.twenty = twenty;}public Seller(int five, int ten, int twenty) {this.five = five;this.ten = ten;this.twenty = twenty;}/***找零* @param target受理对象* @param five 收到的5元纸币数* @param ten 收到的10元纸币数* @param twenty 收到的20元纸币数*/public boolean change(String target,int five,int ten ,int twenty){int pay = five*5+ten*10 +twenty*20;//支付的钱int moneyChange = pay - PRICE;//找零if(moneyChange == 0){//不需要找0System.out.println("售票员收到"+pay+"元钱,递给"+target+"1张入场券\n");this.setFive(this.five + five);return true;}//使用不同币种依次找零//使用20元纸币找零int twentyChange = moneyChange/20;if (this.twenty >= twentyChange) {moneyChange = moneyChange -twentyChange*20;this.setTwenty(this.twenty - twentyChange);}else {moneyChange = moneyChange-this.twenty*20;twentyChange = this.twenty;//钱箱中只能找这么多20块的this.setTwenty(0);//全部拿去找零}//使用10元纸币找零int tenChange = moneyChange/10;if (this.ten >= tenChange) {moneyChange = moneyChange -tenChange*10;this.setTen(this.ten - tenChange);}else {moneyChange = moneyChange-this.ten*10;tenChange = this.ten;//只能找这么多10块的this.setTen(0);//全部拿去找零}//使用5元纸币找零int fiveChange = moneyChange/5;if (this.five >= fiveChange) {moneyChange = moneyChange -fiveChange*5;this.setFive(this.five - fiveChange);//此时可以说明钱箱中的钱足以找零System.out.println("售票员收到"+pay+"元钱,递给"+target+"1张入场券,并找零20*"+twentyChange+"+10*"+tenChange+"+5*"+fiveChange+"元\n");//收钱this.setFive(this.five + five);this.setTen(this.ten +ten);this.setTwenty(this.twenty +twenty);return true; }else {//此时,钱箱中的所有币种加起来都不足以找零return false;} }
}
03 线程初始化
Java中,线程初始化一般有两种方式:
1.继承Thread类,重写run方法
运行线程时,直接使用实例化对象的start方法即可运行线程,原因是Thread类及其子类带有start方法。以下为伪代码
//买票过程
class BuyAndSell extends Thread {//对线程各参数的设定//线程构造方法@Overridepublic void run() {//你要让线程执行的功能,在这里书写 } }
}
public class Main {public static void main(String args[ ]) {//线程构造,这里使用了向上转型Thread zhang = new BuyAndSell();//运行线程zhang.start();}}
2.实现Runable接口,重写run方法
此时BuyAndSell是一个实现了Runnable接口的实现类,需要将该实现类的对象如下面的zhang,传给Thread类,让Thread类构造线程并调用start方法启动线程。以下为伪代码
class BuyAndSell implements Runnable {//设定线程各参数//线程构造方法等@Overridepublic void run() {//你要让线程执行的功能,在这里书写} }
}
public class Main {public static void main(String args[ ]) {//线程构造,使用接口向上转型Runnable zhang = new BuyAndSell();//运行线程new Thread(zhang).start();}}
两种方法都可以构造线程,但是实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,是最优之选。因此,我也使用此方式对线程进行构造
//买票过程
class BuyAndSell implements Runnable {private Seller mb;private String name;//买票人名字private int five; private int ten ;private int twenty;public BuyAndSell(Seller mb, String name, int five, int ten, int twenty) {this.mb = mb;this.name = name;this.five = five;this.ten = ten;this.twenty = twenty;}@Overridepublic void run() {// 对公共资源售票员加锁,每当有一个线程办理业务时,不能被其他线程打扰synchronized (mb) {int pay = five*5+ten*10 +twenty*20;//支付的钱System.out.println(name+"拿出"+pay+"元钱买票");
// try {
// Thread.sleep(100);
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }boolean flag = mb.change(name,five, ten, twenty);while(!flag){System.out.println("钱箱里的钱不足以找零,"+name+"请稍等..\n");try {mb.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(name+"重新拿出"+pay+"元钱买票");flag = mb.change(name,five, ten, twenty);}
// 此时说明办理成功,有零钱了,唤醒其他线程来争夺资源。即叫没有买到票的人来继续买票mb.notifyAll(); } }
}
在run中,使用静态代码块对公共资源售票员加锁,每当有一个受理人办理业务时,不能被其他线程打扰(即该线程拿到了Seller对象mb的锁)。如果钱不够找零(即flag=false)就让这个受理人稍等,即让这个线程wait()(此时,调用wait的同时,也把他原来拿到的锁给释放了)。
有朋友想要继承Thread实现的,我也放这里啦
class BuyAndSell1 extends Thread {private Seller mb;private String name;//买票人名字private int five; private int ten ;private int twenty;public BuyAndSell1(Seller mb, String name, int five, int ten, int twenty) {this.mb = mb;this.name = name;this.five = five;this.ten = ten;this.twenty = twenty;}@Overridepublic void run() {// TODO Auto-generated method stubsynchronized (mb) {int pay = five*5+ten*10 +twenty*20;//支付的钱System.out.println(name+"拿出"+pay+"元钱买票");
// try {
// Thread.sleep(100);
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }boolean flag = mb.change(name,five, ten, twenty);while(!flag){System.out.println("钱箱里的钱不足以找零,"+name+"请稍等..\n");try {mb.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(name+"重新拿出"+pay+"元钱买票");flag = mb.change(name,five, ten, twenty);}
// System.out.println("售票员钱箱里有20元纸币"+mb.getTwenty()+"张,10元纸币"+mb.getTen()+"张,5元纸币"+mb.getFive()+"张\n");mb.notifyAll();}}}
04 业务模拟
主函数中,开始整个业务流程
public class Main {public static void main(String args[ ]) {Seller seller = new Seller(3,0,0);//初始只有3张5元纸币System.out.println("售票员钱箱里有20元纸币"+seller.getTwenty()+"张,10元纸币"+seller.getTen()+"张,5元纸币"+seller.getFive()+"张\n");//构造3个买票过程//Runable 接口实现//张某(拿着20元钞票Runnable zhang = new BuyAndSell(seller,"张某", 0, 0, 1);//李某(拿着10元钞票Runnable li = new BuyAndSell(seller,"李某", 0, 1, 0);//赵某(拿着5元钞票Runnable zhao = new BuyAndSell(seller,"赵某", 1, 0, 0);new Thread(zhang).start();new Thread(li).start();new Thread(zhao).start();/**Thread继承实现//张某(拿着20元钞票Thread zhang = new BuyAndSell1(seller,"张某", 0, 0, 1);//李某(拿着10元钞票Thread li = new BuyAndSell1(seller,"李某", 0, 1, 0);//赵某(拿着5元钞票Thread zhao = new BuyAndSell1(seller,"赵某", 1, 0, 0);zhang.start();li.start();zhao.start();*/}
}
运行结果会随机不同,大概是因为线程会随着cpu时间片的分配随机执行
运行结果1
运行结果2
以上代码,经过本人调试,没有看出问题来。如有问题,多半是编辑错误,欢迎指正!
Java线程同步-模拟买票相关推荐
- Java线程模仿电影院买票
模拟唐僧师徒4人买电影票.电影票10元一张,唐僧拿着一张50元的.孙悟空那和 猪八戒都拿着一张20的. 沙僧拿着一张10元的.此时售票员手中只有一个10元的. 模拟他们4个人买票. package c ...
- 关于java线程同步的笔记_线程同步(JAVA笔记-线程基础篇)
在多线程应用程序中经常会遇到线程同步的问题.比如:两个线程A.线程B可能会 "同时" 执行同一段代码,或修改同一个变量.而很多时候我们是不希望这样的. 这时候,就需要用到线程同步. ...
- java线程同步——竞争条件的荔枝+锁对象
[0]README 0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java线程同步--竞争条件的荔枝+锁对象 的相关知识: 0.2) for full sou ...
- java线程 同步与异步 线程池
1)多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线 程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成的,有时为了解 决此问题,优先考虑 ...
- (转) Java线程同步阻塞, sleep(), suspend(), resume(), yield(), wait(), notify()
为了解决对共享存储区的访问冲突,Java 引入了同步机制.但显然不够,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个. 为解决访问控制问题,Java ...
- java线程同步——条件对象+synchronized 关键字
[0]README 0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java线程同步--条件对象+synchronized 关键字 的相关知识: 0.2)for ...
- Java --- 线程同步和异步的区别
1. Java 线程 同步与异步 多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成的,有 ...
- Java线程同步的几种方式
Java线程同步的几种方式 1.使用synchronized关键字 它的工作是对同步的代码加锁,使得每一次只能有一个线程进入同步块,从而保证线程间的安全性. synchronized关键字的用法: ...
- java线程同步原理
一. java线程同步原理 java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处理这 ...
最新文章
- VirtualBox中修改Ubuntu-server分辨率
- “读书”频道的一些链接错误
- 解决Win7英文版显示中文乱码
- J2EE后台UI系统框架搭建-EXTJs使用(4.1 GPL版本)
- 如何让Excel里显示的数字避免通过科学计数法来显示
- 基于JAVA+SpringMVC+Mybatis+MYSQL的医院管理系统
- InnoDB与Myisam的六大区别
- python打包生成so文件
- JS实现复制到剪贴板功能
- html中qq咨询的代码,QQ样式的在线客服代码
- 做项目管理需要哪些技能?
- Ubantu 安装 Oracle JDK
- 《编码隐藏在计算机软硬件背后的语言》读感
- 如何利用等比频宽公式将信号分解成部分重叠的子频段
- 生动的ajax图片显示效果,LightBox
- linux基础命令学习笔记
- Hive错误之 Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask错误分析
- 我的世界(12)-服务器领地(Residence插件)
- 云服务器可以修改ip,云服务器的ip可以更换吗
- DTS、杜比2.0、杜比5.1、AC3
热门文章
- keil C语言编程 位地址定义,Keil C编写下位机程序的小技巧和注意点
- 2022细胞生物学实验原理复习资料汇总
- 画中画 视频叠加 视频覆盖 overlay
- Excel:某一列前面统一加上相同的内容
- 前端白屏问题_H5白屏问题
- [java]轻量级隔离化的excel导入导出的导入导出,解放导入导出冗余代码(有github源码)
- 高速PCB基础——电阻,电容,电感(第三讲)
- Echarts真正态分布图
- JavaSE学习笔记
- 360极速浏览器2013 v7.5.3.202 官方版