初识并发问题(售票)

我们通过一个下面这个抢电影票的简单例子,认识一下并发问题:

情景描述:多个人去抢20张电影票

代码如下:

package com.kuang.thread;

/**
 * @ClassName TestThread4
 * @Description 初识多线程  售票
 * @Author 麻虾
 * @Date 2021/4/24 15:12 12
 * @Version 1.0
 */

//多个线程同时操作同一个对象
//买火车票的例子
//发现问题,多个线程操作同一个资源的情况下,数据紊乱,线程不安全。如何解决?待续...
public class TestThread4 implements Runnable{

    //票数
    private int ticketNums = 20;

    @Override
    public void run() {
        //线程体
        //买票  只要还有票,就一直拿
        while (true){
            //判断什么时候结束
            if(ticketNums <= 0){
                break;
            }

            //模拟延迟:sleep(毫秒),sleep需要捕获异常
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "---->抢到了第" + ticketNums-- + "张票");
        }
    }

    public static void main(String[] args) {

        TestThread4 testThread4 = new TestThread4();
        
        //创建多个不同名字的线程
        new Thread(testThread4,"主任").start();
        new Thread(testThread4,"局长").start();
        new Thread(testThread4,"书记").start();
        new Thread(testThread4,"县长").start();
        new Thread(testThread4,"老师").start();
        new Thread(testThread4,"校长").start();

    }
}

说一下抢电影票的思路:

  1. 首先,我们实现Runnable接口创建线程,重写run()方法,run里面就是我们的线程执行体
  2. 然后,既然是抢票,自然要定义一个火车票的数量ticketNums = 20
  3. 紧接着,在线程体里面写抢票的逻辑:使用while(true)循环不停地进行抢票,打印出哪个线程抢到了哪张票,每抢到一张票,票数就减1
  4. 那么什么时候while停止呢?就是什么时候抢票停止呢?我们用if语句判断票数是否小于等于0,小于等于0的时候就表示票已经被抢完,那么就要break结束while了
  5. 另外,在这里我们用Thread.sleep(200)模拟延迟,让当前线程睡一会儿再执行。注意:sleep需要捕获异常,用try-catch包裹起来
  6. 最后在主线程里创建了多个线程,并启动它们去抢票

这就是多个线程争夺同一个资源的问题

我们看一下上面这段代码的运行结果:

县长---->抢到了第17张票
书记---->抢到了第20张票
局长---->抢到了第19张票
主任---->抢到了第18张票
校长---->抢到了第16张票
老师---->抢到了第15张票
主任---->抢到了第13张票
局长---->抢到了第12张票
书记---->抢到了第14张票
县长---->抢到了第11张票
老师---->抢到了第10张票
校长---->抢到了第9张票
局长---->抢到了第8张票
书记---->抢到了第8张票
主任---->抢到了第8张票
县长---->抢到了第8张票
老师---->抢到了第7张票
校长---->抢到了第6张票
县长---->抢到了第5张票
局长---->抢到了第5张票
主任---->抢到了第5张票
书记---->抢到了第5张票
老师---->抢到了第4张票
校长---->抢到了第3张票
主任---->抢到了第2张票
局长---->抢到了第2张票
书记---->抢到了第2张票
县长---->抢到了第2张票
校长---->抢到了第1张票
老师---->抢到了第0张票
县长---->抢到了第-1张票
书记---->抢到了第-2张票
局长---->抢到了第-4张票
主任---->抢到了第-3张票

Process finished with exit code 0

从运行结果我们可以看出票8,2,5等被多个人抢到了,这就出现了问题,线程是不安全的。

按理说,应该是同一张票不能被多个人拿到,每个人拿到的都是唯一的一张票。

这个示例就引出了我们的多线程并发,线程安全,以及如何确保线程安全的问题

 

什么是线程安全?

《Java并发编程实践》中对线程安全的定义:

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。

《深入理解Java虚拟机》的作者也认可这个观点。本人也认为这是一个恰当的定义,因为线程安全的主体是什么?是方法还是代码块?这里给出的主体是对象,这是非常恰当的,因为Java是纯面向对象的,Java中一切为对象。因此通过对象定义线程安全是恰当的。

我认同这个观点,我们也可以看一下其他的定义方式,进行一下对比,这样认识会更清晰:

当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类时线程安全的。
如果一段代码可以保证多个线程访问的时候正确操作共享数据,那么它是线程安全的

 

如何解决这个情况?如何确保线程安全?

待续..........

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无霸哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值