需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口买票,请设计一个程序模拟该电影院卖票
思路:
1.定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;
2.在Ticket类中重写run()方法实现卖票,代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:票数要减1
C:卖光之后,线程停止
3.定义一个测试类TicketDemo,里面有main方法,代码步骤如下
A:创建Ticket类的对象
B:创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
打印的顺序是随机的,是因为三个线程在争夺CPU运行权,这不影响程序的正确性
package threaddemo; public class Demo { public static void main(String[] args) { /* Ticket ticket1 = new Ticket(); Ticket ticket2 = new Ticket(); Ticket ticket3 = new Ticket(); Thread t1 = new Thread(); Thread t2 = new Thread(); Thread t3 = new Thread();*/ Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); Thread t3 = new Thread(ticket); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } } //******************************* package threaddemo; public class Ticket implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ if (ticket == 0){ //卖完了 break; }else { ticket --; System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票"); } } } }
卖票案例的思考
刚才讲解了电影院卖票程序,好像没有什么问题。但是在实际生活中,售票时出票也是需要时间的,所以,在出售一张票的时候,需要一点时间的延迟,接下来我们去修改卖票的动作:每次出票时间100毫秒,用sleep()方法实现
加了延时之后
卖票出现了问题
相同的票出现了多次
出现了负数的票
package threaddemo; //错误案例 窗口一在卖票,还剩下0张票 //窗口三在卖票,还剩下-1张票 //窗口二在卖票,还剩下-2张票 public class Ticket implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ if (ticket <= 0){ //卖完了 break; }else { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket --; System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票"); } } } } //**************************分隔符 package threaddemo; //错误案例 窗口一在卖票,还剩下0张票 //窗口三在卖票,还剩下-1张票 //窗口二在卖票,还剩下-2张票 public class Ticket implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ if (ticket <= 0){ //卖完了 break; }else { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket --; System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票"); } } } }
判断条件是 if (ticket == 0)的话,它有时候会一直执行,停不下来
package threaddemo; //错误案例 窗口二在卖票,还剩下-167张票 //窗口一在卖票,还剩下-168张票 //窗口二在卖票,还剩下-169张票 //窗口三在卖票,还剩下-170张票 //窗口一在卖票,还剩下-171张票 //窗口二在卖票,还剩下-172张票 //窗口三在卖票,还剩下-173张票 public class Ticket implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ if (ticket == 0){ //卖完了 break; }else { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket --; System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票"); } } } } //************************ package threaddemo; public class Demo { public static void main(String[] args) { /* Ticket ticket1 = new Ticket(); Ticket ticket2 = new Ticket(); Ticket ticket3 = new Ticket(); Thread t1 = new Thread(); Thread t2 = new Thread(); Thread t3 = new Thread();*/ Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); Thread t3 = new Thread(ticket); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
卖票案例数据安全问题的解决
为什么会出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准)
多线程操作共享数据
如何解决多线程安全问题呢?
基本思想:让程序没有安全问题的环境
怎么实现呢?
把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
Java提供了同步代码块的方式来解决
同步代码块
锁多条语句操作共享数据,可以使用同步代码块实现
synchronized(任意对象){
多条语句操作共享数据的代码
}
默认情况是打开的,只要有一个线程进去执行代码了,锁就会关闭
同步的好处和弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
package threaddemo; //错误案例 窗口二在卖票,还剩下-167张票 //窗口一在卖票,还剩下-168张票 //窗口二在卖票,还剩下-169张票 //窗口三在卖票,还剩下-170张票 //窗口一在卖票,还剩下-171张票 //窗口二在卖票,还剩下-172张票 //窗口三在卖票,还剩下-173张票 public class Ticket implements Runnable{ private int ticket = 100; private Object obj = new Object(); @Override public void run() { while (true){ synchronized (obj){//多个线程必须使用同一把锁 if (ticket == 0){ //卖完了 break; }else { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ticket --; System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket +"张票"); } } } } } //******************************************** package threaddemo; public class Demo { public static void main(String[] args) { /* Ticket ticket1 = new Ticket(); Ticket ticket2 = new Ticket(); Ticket ticket3 = new Ticket(); Thread t1 = new Thread(); Thread t2 = new Thread(); Thread t3 = new Thread();*/ Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); Thread t3 = new Thread(ticket); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }