JAVA学习之路(多线程)---模拟售票(细解)
首先看题目描述:
假设有火车票100张,创建4个线程模拟4个售票点,每100ms售出一张,打印出售票过程,格式如下:
窗口3:卖出第100张票
窗口4:卖出第99张票
............
............
简单的思路就是创建一个类,首先肯定要去继承Thread。开启线程,由于是4个窗口,肯定要开启4个线程。然后让每个线程去输出结果,也就是卖出去的票。这里很多人想不到如何让4个线程不打印重复的票。(比如4个线程都卖出去了第100张票,这显然是不合理的)。
看代码:
1 package com.lesson.thread; 2 3 public class MyThread { 4 5 public static void main(String[] args) { 6 Ticket sell1 = new Ticket(); 7 Ticket sell2 = new Ticket(); 8 Ticket sell3 = new Ticket(); 9 Ticket sell4 = new Ticket(); 10 sell1.setName("窗口1"); 11 sell2.setName("窗口2"); 12 sell3.setName("窗口3"); 13 sell4.setName("窗口4"); 14 sell1.start(); 15 sell2.start(); 16 sell3.start(); 17 sell4.start(); 18 } 19 } 20 class Ticket extends Thread { 21 private static int tickets = 100;//这里设置成static,目的是让每个线程共享这个变量。以免出现重复打印的现象。 22 @Override 23 public void run() { 24 while(true) { 25 if(tickets <= 0) { 26 break; 27 } 2829 System.out.println(getName()+":买出第"+tickets--+"张票。");//卖出一张减一张票 30 31 } 32 } 33 34 }
可能你已经看到了你想要的结果了。但是,还没完。目前代码写到这里是有问题的!!!
为什么?
看下面的代码:
package thread; public class Mythread { public static void main(String[] args) { Ticket sell1 = new Ticket(); Ticket sell2 = new Ticket(); Ticket sell3 = new Ticket(); Ticket sell4 = new Ticket(); sell1.setName("窗口1"); sell2.setName("窗口2"); sell3.setName("窗口3"); sell4.setName("窗口4"); sell1.start(); sell2.start(); sell3.start(); sell4.start(); } } class Ticket extends Thread { private static int tickets = 100; @Override public void run() { while(true) { if(tickets <= 0) { break; } try { Thread.sleep(10); //让进来的线程睡10ms;线程1,2,3,4都睡在这 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName()+":买出了第"+tickets--+"张票。"); } } }
与开始的代码不同在于让进来的线程睡一会。可以看到如下运行结果:
窗口2:买出了第0张票。
窗口3:买出了第-1张票。
窗口1:买出了第-2张票。
这里编者调出来了出问题的地方。可以看到怎么会第0,-1,-2张票???
做出解释:
其实在这里让线程睡一会就是为了演示这里有很多行代码要执行。假设票已经卖到第1张了,也就是tickets=1,然后第一条线程进来判断 tickets <= 0 ?不成立,然后线程1要睡 10ms,紧接着,线程2进来,这时 tickets 还是为1,因为线程1在睡,tickets 没有减 。然后线程2判断 tickets <= 0 ? 还是不成立,线程2又开始睡。同样,线程3,线程4都睡了。 这时的tickets 还是等于1的。然后线程1先醒过来,开始输出结果,tickets 减了1。可是这是其他的线程还是经过while里面的判断语句进来了的,只是睡了。所以当其他线程醒过来的时候,还是会打印出结果的。也就出现了上面的问题。
解决方法:
多线程并发改变同一变量,为了解决,采用同步代码块synchronized。里面加任意的对象,但是不能加this,因为这里创建了四个线程,每一个线程都有自己的对象,所以是四个不同的对象,没有用。所以这里不能用this,必须锁在同一个对象里才行。而Thickets.class这是唯一的。
package thread; public class Mythread { public static void main(String[] args) { Ticket sell1 = new Ticket(); Ticket sell2 = new Ticket(); Ticket sell3 = new Ticket(); Ticket sell4 = new Ticket(); sell1.setName("窗口1"); sell2.setName("窗口2"); sell3.setName("窗口3"); sell4.setName("窗口4"); sell1.start(); sell2.start(); sell3.start(); sell4.start(); } } class Ticket extends Thread { private static int tickets = 100; @Override public void run() { while(true) { synchronized (Ticket.class) {//同步代码块 if(tickets <= 0) { break; } try { Thread.sleep(100); //每100ms卖出一张 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName()+":买出了第"+tickets--+"张票。"); } } } }