栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java多线程(一)------深入剖析为什么你写的卖票线程会出现负数

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java多线程(一)------深入剖析为什么你写的卖票线程会出现负数

     一、问题描述   

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

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

        二、错误代码

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

        上代码:

package com.example.demo.threadTest;


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

         输出结果如下:

        可以看出,确实存在有两条记录错误。那么我错误的原因是什么呢?在卖票类里面我也进行了数字判断,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。

 

 

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

        四、写在最后

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/324554.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号