多线程并发问题 黄牛卖票和模拟龟兔赛跑问题分析

数据共享带来的并发问题

多线程有两个经典案例,12306买票问题和上厕所问题😂

一份资源,多个线程共享,会带来并发问题(数据的不一致性)

在实例化Web12306的时候,要想达到20张票,3个黄牛在卖,就只能实例化一次;如果实例化三次,则成为每个黄牛在卖20张票,不会产生并发问题,因为此时每一个线程都得带到了一份资源。

为了模拟买票时的网络延迟,Sleep 200ms

但得的结果却是:【不存在的票】

20份票的资源给3个黄牛来卖,票显然不会出现负数,这就是并发带来的数据不一致的问题

具体的原因:

首先我们要明白的一点就是,CPU执行的速度非常的快,用ms甚至是ns来衡量

当只剩最后一张票时,假设线程1、线程2、线程3分别被CPU调度:

  1. tickets = 1 > 0,线程1先进入到while循环中,休眠200ms,交出了CPU执行权;
  2. 此时,CPU空闲,调度线程2,由于线程1在休眠中还没有卖出票,此时 tickets = 1;
  3. 线程2进入while循环,也休眠200ms。此时tickets仍然等于1;
  4. 由于CPU切换的速度非常快,此时线程1和线程2仍然在休眠中,CPU调度线程3;
  5. 线程3进入while循环,同样休眠200ms
  6. 线程1最先休眠,所以最先醒来。执行tickets--操作,tickets = 0,此时线程2和线程3仍在休眠中
  7. 线程2睡眠完成,由于线程2和线程3此时已经进入到了循环中,所以可以继续执行。线程2执行tickets--操作,tickets = -1
  8. 同理,线程三醒来后又执行tickets--tickets = -2

还有可能得到的结果是:【卖重复的票】

同一份资源,被三个线程同时拿到

每个线程在工作的时候,都会在相应的栈中执行,一个线程开辟一个栈空间(忘了的伙伴看看上面线程执行的内存图),它们从内存中读取数据。

比如在卖第10张票时,A线程从主存中拿到了10;由于CPU的操作非常的块,在A把10拿到他的工作区间的时候,线程B和线程C也同时从主存中拿到了各自的工作区间;所以就导致第10张票被卖了3次…


注意:

线程安全问题是不能产生的,我们可以让一个线程在访问共享数据的时候,无论是否失去了对CPU的执行权,让其他的线程只能等待当前占有CPU的线程执行完其所有的操作,其他线程才能获取资源(等待当前线程卖完票,其他线程在进行卖票)

保证:同一时刻只有一个线程在卖票,即在上面的例子中,只有一个线程在while循环中进行买票

所以,针对此问题,Java中提出了锁机制

通过synchronized关键字来给对象上锁

👉线程的同步👈


多线程模拟实现龟兔赛跑

故事背景:
从前有座山,山上有个乌龟和兔子,然后他们比赛,然后兔子睡着了,然后乌龟赢了,然后就没有然后了…

class Running implements Runnable {
    private  String winner;  //获胜者名字
    @Override
    public void run() {
        for(int steps = 1;steps<=100;steps++) {
            //模拟兔子休息,让兔子每走10步睡300ms
            if(Thread.currentThread().getName().equals("rabbit") && steps%10==0) {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() +"---->" + steps);
            //判断比赛是否结束
            boolean flag = isOver(steps);
            if(flag)
                break;
        }
    }
    
    private boolean isOver(int steps) {
        if(winner != null)
            return true;
        else {
            if(steps == 100) {
                winner = Thread.currentThread().getName();
                System.out.println("\n***** Winner is " + winner + " *****");
                return true;
            }
        }
        return false;
    }
}


public class ThreadExample {
    public static void main(String[] args) {
        Running run = new Running();
        new Thread(run,"tortoise").start();
        new Thread(run,"rabbit").start();
    }
}

最终,兔子走了10步,乌龟走了100步,Winner为 乌龟

这个故事告诉我们:

  1. 腿长的不一定走得快
  2. 时间再短也可以睡觉
  3. 在这个故事里,赢得总会是乌龟;因为编故事的人不想让兔子赢,不然还比什么赛呀…兔子要想变的像博尔特辣样快的蓝人,还和乌龟比什么,直接去找猎豹比了,破釜沉舟…
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值