Java多线程(一)------卖票出现负数

     一、问题描述   

        卖票问题作为多线程入门问题,想必大家都不陌生。对于一个新入门多线程的小白来讲,在写代码的时候难免会出现各种问题。而我就出现了卖票结果出现0和负数的情况, 话不多说,截图为证:

Java多线程(一)------卖票出现负数

         上述是新建了五个线程,但是有四条结果是错误的。如果按照我这种写法,那么不管是新建多个个线程,最终都会有n-1条结果错误。如果你也跟我有同样的问题,就请继续看下去吧。

        二、错误代码

        首先我错误的写法是怎么样的呢?

        上代码:

package com.example.demo.threadTest;

/**
 * @ClassName: Test7
 * @Author: wnn
 * @Description: 三个售票窗口同时出售20张票
 * @Date: 2021/10/14 14:11
 */
public class Test7 {
    public static void main(String[] args) {
        SellTickets sellTickets = new SellTickets();
        //创建三个线程
        for (int i = 1; i <= 3; i++) {
            Thread thread = new Thread(sellTickets, "线程" + i);
            thread.start();
        }
    }
}

//卖票类
class SellTickets1 implements Runnable {
    static int tickets = 20;
    static Object object = new Object();

    @Override
    public void run() {
        while (tickets > 0) {
            synchronized (object) {
                System.out.println(System.currentTimeMillis() + "-----" + Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
                tickets--;
                try {
                    Thread.sleep(1000);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}

         输出结果如下:

Java多线程(一)------卖票出现负数

        可以看出,确实存在有两条记录错误。那么我错误的原因是什么呢?在卖票类里面我也进行了数字判断,tickets需要大于0,那么在最后1张票卖出后,应该不会继续执行while循环。然后结果却并非我预想的那样。

        三、原因解答

        经过我的苦思冥想,反复论证,我终于算是有了自己对于多线程的理解。

        创建的三个线程会一起执行代码,也就是run()方法里面的while判断,发现大于0的时候会进入循环体,但是由于synchronized同步代码块的锁的存在,就意味着同一时刻只能有一个线程去卖票。

        但是,重点来了。虽然进入循环体的线程可能没有占据CPU,进入同步代码块,但是却已经进入循环体,只要占有锁,即可执行ticket--的操作。

        所以说,等某一线程卖完最后一张票并释放锁资源后,在此之前已经进入循环体的线程会再次争夺cpu继续卖票,直到所有进入循环体的线程都进入过同步代码块,卖过票为止。

        如果对于上述的表达还是不明白的话,可以看看改编后的代码(只放出卖票类):

//卖票类
class SellTickets implements Runnable {
    static int tickets = 20;
    static Object object = new Object();
    @Override
    public void run() {
        while (tickets > 0) {
            System.out.println(System.currentTimeMillis() + "-----" +Thread.currentThread().getName()); //用来判断同一时刻进入的线程
            synchronized (object) {
                if (tickets ==0) {
                    System.exit(0);//退出程序
                }
                System.out.println(System.currentTimeMillis() + "-----" + Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
                tickets--;
                try {
                    Thread.sleep(1000);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}

         上述代码执行的执行结果,可以自己去试一下。

         下面我就结果进行说明。从下图结果可以知道,在28时刻,确实有两个线程进入了循环体,但是由于线程1获取到了锁,所以进入同步代码块卖票。但是线程2会继续在循环体中争夺cpu。

 Java多线程(一)------卖票出现负数

 Java多线程(一)------卖票出现负数

         然后添加if判断后,即使线程进入了同步代码块,但是由于票已卖完,就会强制执行退出操作,那么结果就不会继续减少了。

Java多线程(一)------卖票出现负数

        四、写在最后

        不知道我的表达,你是否明白,欢迎交流。 

上一篇:Linux vnc


下一篇:基于java的个人博客系统的设计与实现