今天自己按照网上多线程实现卖票的逻辑写了下代码,因为没有完全照抄,最后发现结果竟然错了!!!
下面就是我写的代码
public class ThreadDemo {
public static void main(String[] args) {
ThreadTrain threadTrain = new ThreadTrain();
Thread t1 = new Thread(threadTrain,"一号窗口");
Thread t2 = new Thread(threadTrain,"二号窗口");
t1.start();
t2.start();
}
}
class ThreadTrain implements Runnable {
/**
* 总票数
*/
private int count = 100;
private Object obj = new Object();
@Override
public void run() {
while (count > 0) {
sale();
}
}
/**
* 卖票
*/
private void sale() {
//对代码块加锁
synchronized (obj) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (101 - count) + "票");
--count;
System.out.println(count);
}
}
}
最后结果
可以看到count=0的时候也进循环了,导致最后多卖了一张票。
后来问同事才知道原来这也是多线程导致的,因为count=1时可能两个线程都进入了while判断,其中一个线程获得锁执行锁住的代码,另一个线程在加锁代码块前阻塞。获得锁的线程执行完后count值为0,释放锁。阻塞的线程获取锁,用count=0继续进行运算,导致结果错误。修改的方法有多种,不过最简单的就是在锁代码块中再加一个判断,修改后的代码如下
public class ThreadDemo {
public static void main(String[] args) {
ThreadTrain threadTrain = new ThreadTrain();
Thread t1 = new Thread(threadTrain,"一号窗口");
Thread t2 = new Thread(threadTrain,"二号窗口");
t1.start();
t2.start();
}
}
class ThreadTrain implements Runnable {
/**
* 总票数
*/
private int count = 100;
private Object obj = new Object();
@Override
public void run() {
while (count > 0) {
sale();
}
}
/**
* 卖票
*/
private void sale() {
//对代码块加锁,可能有一个线程获得锁了,另一个线程阻塞在这,等待线程释放锁后继续执行。
synchronized (obj) {
//双重判断,因为多个线程可能都进入了while循环,虽然只有一个执行锁住的代码,但其他线程会在释放锁后继续执行,而无需再通过while判断导致结果出错。
if (count>0){
System.out.println(Thread.currentThread().getName() + ",出售第" + (101 - count) + "票");
--count;
System.out.println(count);
}
}
}
}
网上有些代码写的是在sale();语句后让线程sleep一会,不过个人感觉不是很好。