在入门多线程的时候,看到过不少的案例,其中卖票案例尤为经典,在这里自己也记录一下,同时加深对于线程安全的理解:

案例场景
  1. 情景一:

   现在有一个电影院,马上要上映电影《战狼5》,电影院只售票100张,全部通过一个窗口卖出去,那么此种情况则不会出现电影票超卖或者其他任何情况。

  1. 情景二:

  现在电影院装修升级了,新增了两个卖票窗口,1号窗口卖1-30号票,2号窗口卖31-60号票,3号窗口卖61-100号票。相当于三个窗口各卖各的,互不干扰。所以此种情况也不会产生任何问题。

  1. 情景三:

   现在卖票的方式发生了改变,3个窗口都卖1-100号票,那现在就可能会出现一些情况,窗口1在卖100号票的时候(正在和顾客沟通中),3号窗口一看该卖100号票了(最后一张),啪!直接将100号票给了顾客。此时窗口1与顾客沟通确认无误之后,打算出票了,结果一看,100号票么得了~顾客当场开始砸场子……

  情景三中,我们三个窗口相当于是三个线程,共同去卖那100张票相当于就是共同访问同一份共享资源。在这种条件下,就可能出现我们所说的线程安全问题(线程安全发生的条件:多线程共同访问同一份共享资源

情景三代码实现
public class RunnableImpl implements Runnable {//给一个共享资源private int ticket = 100;//指定线程所要执行的任务(卖票)@Overridepublic void run() {while (true) {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张电影票");try {//为了卖票出错的情况更加明显,我们让线程等待100msThread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}--ticket;} else {System.out.println("票卖完了");return;}}}
}
public class FileTest {public static void main(String[] args) {//思考:为什么这里只new 了一个Runnable的对象传到了三个线程种?RunnableImpl runnable = new RunnableImpl();//new 三个线程出来模三个窗口,为了符合情景,我们为线程设置一个线程名Thread thread1 = new Thread(runnable);thread1.setName("窗口1");Thread thread2 = new Thread(runnable);thread2.setName("窗口2");Thread thread3 = new Thread(runnable);thread3.setName("窗口3");thread1.start();thread2.start();thread3.start();}}

 在上面的代码中,我们有一个思考:为什么这里只new 了一个Runnable的对象传到了三个线程种?

  因为我们需要让三个窗口来共同卖那100张票,所以他们访问的是同一个共享资源,如果这里new 了3个Runnable对象并分别放到3个Thread对象中(3个不同的窗口中),那三个窗口访问就不是同一份资源了,既然不是同一份资源,也就不满足线程安全问题产生的条件。自然不会产生线程安全问题。

启动之后,我们发现三个窗口直接把票卖重了,都在卖100号票,这不是搞事情嘛……

电影院为了避免出现上面这种卖票混乱的情况,决定对售票系统进行优化:

安全卖票代码实现
public class RunnableImpl implements Runnable {//给一个共享资源private int ticket = 100;//指定线程所要执行的任务(卖票)@Overridepublic void run() {while (true) {//这里我们使用synchronized关键字来锁定卖票系统synchronized (this) {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张电影票");try {//为了卖票出错的情况更加明显,我们让线程等待100msThread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}--ticket;} else {System.out.println("票卖完了");return;}}}}
}
public class FileTest {public static void main(String[] args) {//思考:为什么这里只new 了一个Runnable的对象传到了三个线程种?RunnableImpl runnable = new RunnableImpl();//new 三个线程出来模三个窗口,为了符合情景,我们为线程设置一个线程名Thread thread1 = new Thread(runnable);thread1.setName("窗口1");Thread thread2 = new Thread(runnable);thread2.setName("窗口2");Thread thread3 = new Thread(runnable);thread3.setName("窗口3");thread1.start();thread2.start();thread3.start();}}

启动项目之后:

总结1:
  这次电影院将售票系统升级之后,对售票系统进行了改进,当窗口1在使用系统进行卖票的时候(窗口1抢到CPU的执行权),系统就锁定了,窗口2和窗口3无法卖票(等待窗口1释放CPU的执行权),当窗口1将票卖完了之后,系统就会开放(窗口1释放了CPU的执行权),此时,窗口1、窗口2、窗口3又可以使用系统卖票。假如此时窗口3的动作快一点进入了系统(窗口3抢到CPU的执行权,当然窗口1虽然刚用完系统,但是也有可能窗口1又进入系统,相当于窗口1又抢到了CPU的执行权),这样一来,在同一时间内,只会有一个窗口在卖票(同一时间,只有一个线程能够访问某个共享资源,其它线程只能等待抢到CPU执行权的线程释放资源)。也就不会产生电影票的超卖等安全问题(线程安全问题)。

总结2:
  我们在卖票系统升级的代码中,使用了synchronized(this){……}同步代码块来实现了对卖票系统的锁定(对共享资源的锁定),那么这个this代表的是什么呢?
  答案就是这个this代表了当前正在执行这段代码的线程对象,这个锁就是同步锁。在java中,每个对象有且仅有一个对象锁。那么我们锁定卖票系统的原理是什么呢?
  分析:窗口1执行到这里遇到synchronized同步代码块,检查synchronized是否有锁对象,发现有因为是第一个执行的,进入同步代码块中执行卖票的代码。在窗口1执行的过程中,窗口2来到这里,发现synchronized同步代码块,检查是否有锁对象,发现没有。则窗口2线程进入阻塞状态,等待窗口1线程执行完毕释放锁对象。

本篇文章是使用synchronized同步锁实现了安全卖票,下一篇文章使用Lock锁实现相同的效果。

线程安全问题经典案例---卖票相关推荐

  1. 多线程经典问题 卖票问题

    /** 需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票 思路: ① 定义一个类Ticket实现Runnable接口,里面定义一个成员变量:pri ...

  2. Java线程思想模拟电影院卖票

    需求: 某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一一个程序模拟该电影院卖票 思路:         ①定义一个类SellTicket实现Runnable接口, 里面定义 ...

  3. DHV展示故事经典 案例 卖猪借宿

    一男赶集卖猪,天黑遇雨,二十头猪(DHV展示)未卖成,到一农家借宿.         SF说:家里只一人不便.  (升起防护罩)       男:求你了大妹子,给猪一头.   (类似假性时间限制解除防 ...

  4. 线程编程——经典案例

     生产者消费者问题 这是一个非常经典的多线程题目,题目大意如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个有多个缓冲区的缓冲池, ...

  5. 线程安全经典案例:银行取钱问题

    银行取钱的基本流程基本上可以分为如下几个步骤. (1)用户输入账户.密码,系统判断用户的账户.密码是否匹配. (2)用户输入取款密码 (3)系统判断账户余额是否大于取款余额 (4)如果余额大于取款余额 ...

  6. Java线程安全(卖票案例) 如何解决线程安全(synchronized ,显示锁Lock)

    文章目录 线程安全 解决方法: 1.同步代码块 2.同步方法 3.显示锁 显示锁与隐式锁的区别 4.公平锁与非公平锁 线程安全 经典问题:卖票问题,多个线程一起执行该任务,当余票只有1一张时,三个线程 ...

  7. java线程池售票_Java多线程卖票例子

    以卖票的例子来介绍多线程和资源共享. 卖票是包含一系列动作的过程,有各种操作,例如查询票.收钱.数钱.出票等,其中有一个操作是每次卖掉一张,就将总的票数减去1.有10张票,如果一个人卖票,先做查票.收 ...

  8. 12.多线程的实现方式、线程安全问题的产生与解决以及生产者与消费者问题

    一.实现多线程 1.1 了解多线程 多线程是指从软件或者硬件上实现多个线程并发执行的技术,具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,从而提升性能. 1.2 并发与并行 并行是指同 ...

  9. java多线程【线程安全问题】

    (一)问题描述         我们 先通过代码案例看线程执行过程中会发生那些安全问题 模拟电影院卖票: 某电影院目前正在上映国产大片,共有100张票,而它有3个平台卖票,请设计一个程序模拟该电影院卖 ...

最新文章

  1. linux 反弹shell(二)反弹shell的本质
  2. C语言图形界面的编程
  3. Bitcoin 地址原理(1)Base58编码
  4. 【原创】flume-1.3.0安装配置以及flume启动说明
  5. Centos:netstat or ifconfig 使用不了 最小化安装后无法联网
  6. 第12个双11,天猫的“造新”运动
  7. 【QGIS入门实战精品教程】4.6:QGIS实现栅格(影像、DEM)的拼接与掩膜提取
  8. SVN中Revert changes from this revision 跟Revert to this revision
  9. 杀死 Oculus ,Facebook 改名 Meta ,是押注元宇宙还是“金蝉脱壳”?
  10. 【算法】数组与矩阵问题——找到无序数组中最小的k个数
  11. 读取PDF的文字--zt
  12. 架构即未来 - 组织的设置 读书笔记
  13. FdfsConnectException:无法获取服务端资源:cant creat connection to /ip timeout
  14. mstsc远程桌面全频或自定义窗口
  15. 机器人学重点知识点总结
  16. 微信小程序 flex:1表示什么
  17. NTP时钟源(GPS时间源)介绍与分析
  18. 微信小程序获取位置信息基于腾讯地图实现
  19. Unified Functional Testing(UFT)15.0.2入门保姆级教程(二),图文详解。QTP
  20. 【Java 面试合集】接口以及抽象类

热门文章

  1. 揭露丑恶:中国社会的职业道德集体缺失?
  2. odoo之审批 流程管理 模块
  3. Neo4j和Cypher批量更新和批量插入优化
  4. 双DSP6657+FPGA K7架构电路方案
  5. java中写定时任务
  6. 吐血整理:人工智能、机器学习领域13个常见概念
  7. 微信服务商和渠道商区别
  8. 智能驾驶走上时代风口,数据堂助力汽车更具“智慧”
  9. HDU3629(凸四边形的个数)
  10. mysql 的基本表的应用_Mysql的基本应用笔记1