多线程同步卖票问题遇到的疑惑并解决

今天自己按照网上多线程实现卖票的逻辑写了下代码,因为没有完全照抄,最后发现结果竟然错了!!!

下面就是我写的代码

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);
        }
    }
}

最后结果

多线程同步卖票问题遇到的疑惑并解决_第1张图片

可以看到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一会,不过个人感觉不是很好。

你可能感兴趣的:(多线程)