一、问题案例
某电影院共有100张票,而它有3个窗口卖票,设计一个程序模拟电影院卖票
问题:同一窗口卖完所有的票
代码:
public class SellTicket implements Runnable { private int tickets = 100; private Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (tickets > 0) { //通过sleep来模拟出票时间 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } } } }
测试类:
public class SellTicketDemo { public static void main(String[] args) { //创建SellTicket类的对象 SellTicket st = new SellTicket(); //创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称 Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); //启动线程 t1.start(); t2.start(); t3.start(); } }
运行结果:
二、解决方法
把模拟出票时间放到同步代码块外面,就能看见3个窗口在同时卖票。
代码:
public class SellTicket implements Runnable { private int tickets = 100; private Object obj = new Object(); @Override public void run() { while (true) { //通过sleep来模拟出票时间 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj) { if (tickets1 > 0) { System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } } } }
运行结果:
三、原因分析
在第一个的运行结果中,窗口1进入Runnable(可运行状态),很明显是窗口1线抢到了持有锁,此时,尝试获取锁,窗口1成功拿到锁并执行程序进行卖票;
而在窗口1之后启动的窗口2和3在启动之后也尝试去获取锁,发现锁已经被别人拿走了,于是进入锁池(注意:在同步状态下,没有获取到锁的会进入到等待池,锁池会比等待池中的其他线程更优先于去获取锁);
在窗口1卖完1张票之后,开始释放锁,此时,窗口2和3同时发现锁空了,正准备上去抢,结果窗口1发现自己又需要锁了,于是再次获取锁,窗口1一直围绕在锁的旁边,而窗口2和3在锁池中的板凳上(等待池)上坐着,肯定没有窗口1抢的快。
所以就造成了同一窗口一直卖票的情况。