目录
一、问题案例
某电影院共有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抢的快。
所以就造成了同一窗口一直卖票的情况。