下面这个例子是一个多线程买票的错误示例:
public class BadSaleTicketMutiThread implements Runnable {
int tickets = 100;
int temp = tickets;
boolean flag = true;
@Override
public void run() {
while (flag) {
if (tickets > 0) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
sale();
} else {
flag = false;
System.out.println(Thread.currentThread().getName() + "卖光了");
}
}
}
public synchronized void sale() {
tickets--;
System.out.println(Thread.currentThread().getName() + " 已卖"
+ (temp - tickets) + "张,系统还剩" + tickets + "张票");
}
public static void main(String[] args) {
BadSaleTicketMutiThread st = new BadSaleTicketMutiThread();
new Thread(st, "一号窗口").start();
new Thread(st, "二号窗口").start();
new Thread(st, "三号窗口").start();
new Thread(st, "四号窗口").start();
}
}
最后会运行出的结果会出现负数:
......
四号窗口 已卖99张,系统还剩1张票
一号窗口 已卖100张,系统还剩0张票
一号窗口卖光了
三号窗口 已卖101张,系统还剩-1张票
四号窗口 已卖102张,系统还剩-2张票
二号窗口 已卖103张,系统还剩-3张票
分析:
当线程1 执行到sleep时候,线程2有机会占用cpu(也可能是线程3、线程4),线程2执行到sleep时候,线程3有机会占用cpu(也可能是线程4),线程3到sleep时候,线程4有机会占用cpu,线程4到sleep时候,线程1可能刚好醒来,继续向下执行,调用sale方法,执行tickets-- ,其他线程也陆续的醒来,执行tickets-- ,当tickets=1的时候,假设是线程1 在运行,判断 1>0 ,条件成立,线程1 执行到sleep时,让其线程2、3、4有机会执行,这时候tickets的值还是1,因为 条件判断语句之后就是sleep,故所有的线程都有机会进入到条件块中,所以每个线程都有调用sale方法,即使tickets=0时,tickets还会自减三次。 也就是票数会出现 -1 、-2 、-3的情况 。
解决方法:
将取票后sleep与票数减一同步处理,就可以避免最后一张票卖出去却因票数未减一而导致其他窗口取票动作。
修改后成功运行:
最后会运行出的结果:
......
四号窗口 已卖99张,系统还剩1张票
一号窗口 已卖100张,系统还剩0张票
一号窗口卖光了