当前位置:首页 > 科技  > 软件

喝了100杯酱香拿铁,我开窍了

来源: 责编: 时间:2023-10-10 18:31:52 186观看
导读大家好,我是哪吒。上一篇提到了锁粒度的问题,使用“越细粒度的锁越好”,真的是这样吗?会不会产生一些其它问题?先说结论,可能会产生死锁问题。下面还是以购买酱香拿铁为例:1、定义咖啡实体类Coffee@Datapublic class Coffee

qmY28资讯网——每日最新资讯28at.com

大家好,我是哪吒。qmY28资讯网——每日最新资讯28at.com

上一篇提到了锁粒度的问题,使用“越细粒度的锁越好”,真的是这样吗?会不会产生一些其它问题?qmY28资讯网——每日最新资讯28at.com

先说结论,可能会产生死锁问题。

下面还是以购买酱香拿铁为例:qmY28资讯网——每日最新资讯28at.com

qmY28资讯网——每日最新资讯28at.com

1、定义咖啡实体类Coffee

@Datapublic class Coffee {    // 酱香拿铁    private String name;    // 库存    public Integer inventory;    public ReentrantLock lock = new ReentrantLock();}

2、初始化数据

private static List<Coffee> coffeeList = generateCoffee();public static List<Coffee> generateCoffee(){    List<Coffee> coffeeList = new ArrayList<>();    coffeeList.add(new Coffee("酱香拿铁1", 100));    coffeeList.add(new Coffee("酱香拿铁2", 100));    coffeeList.add(new Coffee("酱香拿铁3", 100));    coffeeList.add(new Coffee("酱香拿铁4", 100));    coffeeList.add(new Coffee("酱香拿铁5", 100));    return coffeeList;}

3、随机获取n杯咖啡

// 随机获取n杯咖啡private static List<Coffee> getCoffees(int n) {    if(n >= coffeeList.size()){        return coffeeList;    }    List<Coffee> randomList = Stream.iterate(RandomUtils.nextInt(n), i -> RandomUtils.nextInt(coffeeList.size()))            .distinct()// 去重            .map(coffeeList::get)// 跟据上面取得的下标获取咖啡            .limit(n)// 截取前面 需要随机获取的咖啡            .collect(Collectors.toList());    return randomList;}

4、购买咖啡

private static boolean buyCoffees(List<Coffee> coffees) {    //存放所有获得的锁    List<ReentrantLock> locks = new ArrayList<>();    for (Coffee coffee : coffees) {        try {            // 获得锁3秒超时            if (coffee.lock.tryLock(3, TimeUnit.SECONDS)) {                // 拿到锁之后,扣减咖啡库存                locks.add(coffee.lock);                coffeeList = coffeeList.stream().map(x -> {                 // 购买了哪个,就减哪个                    if (coffee.getName().equals(x.getName())) {                        x.inventory--;                    }                    return x;                }).collect(Collectors.toList());            } else {                locks.forEach(ReentrantLock::unlock);                return false;            }        } catch (InterruptedException e) {        }    }    locks.forEach(ReentrantLock::unlock);    return true;}

3、通过parallel并行流,购买100次酱香拿铁,一次买2杯,统计成功次数

public static void main(String[] args){    StopWatch stopWatch = new StopWatch();    stopWatch.start();    // 通过parallel并行流,购买100次酱香拿铁,一次买2杯,统计成功次数    long success = IntStream.rangeClosed(1, 100).parallel()            .mapToObj(i -> {                List<Coffee> getCoffees = getCoffees(2);                //Collections.sort(getCoffees, Comparator.comparing(Coffee::getName));                return buyCoffees(getCoffees);            })            .filter(result -> result)            .count();    stopWatch.stop();    System.out.println("成功次数:"+success);    System.out.println("方法耗时:"+stopWatch.getTotalTimeSeconds()+"秒");    for (Coffee coffee : coffeeList) {        System.out.println(coffee.getName()+"-剩余:"+coffee.getInventory()+"杯");    }}

qmY28资讯网——每日最新资讯28at.com

耗时有点久啊,20多秒。qmY28资讯网——每日最新资讯28at.com

数据对不对?qmY28资讯网——每日最新资讯28at.com

  • 酱香拿铁1卖了53杯。
  • 酱香拿铁2卖了57杯。
  • 酱香拿铁3卖了20杯。
  • 酱香拿铁4卖了22杯。
  • 酱香拿铁5卖了19杯。
  • 一共卖了171杯。

数量也对不上,应该卖掉200杯才对,哪里出问题了?qmY28资讯网——每日最新资讯28at.com

4、使用visualvm测一下:

果不其然,出问题了,产生了死锁。qmY28资讯网——每日最新资讯28at.com

线程 m 在等待的一个锁被线程 n 持有,线程 n 在等待的另一把锁被线程 m 持有。qmY28资讯网——每日最新资讯28at.com

  1. 比如美杜莎买了酱香拿铁1和酱香拿铁2,小医仙买了酱香拿铁2和酱香拿铁1;
  2. 美杜莎先获得了酱香拿铁1的锁,小医仙获得了酱香拿铁2的锁;
  3. 然后美杜莎和小医仙接下来要分别获取 酱香拿铁2 和 酱香拿铁1 的锁;
  4. 这个时候锁已经被对方获取了,只能相互等待一直到 3 秒超时。

qmY28资讯网——每日最新资讯28at.com

5、如何解决呢?

让大家都先拿一样的酱香拿铁不就好了。让所有线程都先获取酱香拿铁1的锁,然后再获取酱香拿铁2的锁,这样就不会出问题了。qmY28资讯网——每日最新资讯28at.com

也就是在随机获取n杯咖啡后,对其进行排序即可。qmY28资讯网——每日最新资讯28at.com

// 通过parallel并行流,购买100次酱香拿铁,一次买2杯,统计成功次数long success = IntStream.rangeClosed(1, 100).parallel()        .mapToObj(i -> {            List<Coffee> getCoffees = getCoffees(2);            // 根据咖啡名称进行排序            Collections.sort(getCoffees, Comparator.comparing(Coffee::getName));            return buyCoffees(getCoffees);        })        .filter(result -> result)        .count();

6、再测试一下

  • 成功次数100。
  • 咖啡卖掉了200杯,数量也对得上。
  • 代码执行速度也得到了质的飞跃,因为不用没有循环等待锁的时间了。

qmY28资讯网——每日最新资讯28at.com

看来真的不是越细粒度的锁越好,真的会产生死锁问题。通过对酱香拿铁进行排序,解决了死锁问题,避免循环等待,效率也得到了提升。qmY28资讯网——每日最新资讯28at.com

qmY28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-12735-0.html喝了100杯酱香拿铁,我开窍了

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: .Net析构函数再论(源码剖析)

下一篇: 【JVM问题排查】JDK命令行工具详解,这四个工具你都会用吗?

标签:
  • 热门焦点
  • 小米平板5 Pro 12.4简评:多专多能 兼顾影音娱乐的大屏利器

    小米平板5 Pro 12.4简评:多专多能 兼顾影音娱乐的大屏利器

    疫情带来了网课,网课盘活了安卓平板,安卓平板市场虽然中途停滞了几年,但好的一点就是停滞的这几年行业又有了新的发展方向,例如超窄边框、高刷新率、多摄镜头组合等,这就让安卓
  • 分布式系统中的CAP理论,面试必问,你理解了嘛?

    分布式系统中的CAP理论,面试必问,你理解了嘛?

    对于刚刚接触分布式系统的小伙伴们来说,一提起分布式系统,就感觉高大上,深不可测。而且看了很多书和视频还是一脸懵逼。这篇文章主要使用大白话的方式,带你理解一下分布式系统
  • 多线程开发带来的问题与解决方法

    多线程开发带来的问题与解决方法

    使用多线程主要会带来以下几个问题:(一)线程安全问题  线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程
  • 只需五步,使用start.spring.io快速入门Spring编程

    只需五步,使用start.spring.io快速入门Spring编程

    步骤1打开https://start.spring.io/,按照屏幕截图中的内容创建项目,添加 Spring Web 依赖项,并单击“生成”按钮下载 .zip 文件,为下一步做准备。请在进入步骤2之前进行解压。图
  • WebRTC.Net库开发进阶,教你实现屏幕共享和多路复用!

    WebRTC.Net库开发进阶,教你实现屏幕共享和多路复用!

    WebRTC.Net库:让你的应用更亲民友好,实现视频通话无痛接入! 除了基本用法外,还有一些进阶用法可以更好地利用该库。自定义 STUN/TURN 服务器配置WebRTC.Net 默认使用 Google 的
  • 每天一道面试题-CPU伪共享

    每天一道面试题-CPU伪共享

    前言:了不起:又到了每天一到面试题的时候了!学弟,最近学习的怎么样啊 了不起学弟:最近学习的还不错,每天都在学习,每天都在进步! 了不起:那你最近学习的什么呢? 了不起学弟:最近在学习C
  • 腾讯盖楼,字节拆墙

    腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之&ldquo;想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!&rdquo;曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • iQOO Neo8 Pro即将开售:到手价3099元起 安卓性能最强旗舰

    iQOO Neo8 Pro即将开售:到手价3099元起 安卓性能最强旗舰

    5月23日,iQOO如期举行了新品发布会,全新的iQOO Neo8系列也正式与大家见面,包含iQOO Neo8和iQOO Neo8 Pro两个版本,其中标准版搭载高通骁龙8+,而Pro版更
  • SN570 NVMe SSD固态硬盘 价格与性能兼具

    SN570 NVMe SSD固态硬盘 价格与性能兼具

    SN570 NVMe SSD固态硬盘是西部数据发布的最新一代WD Blue系列的固态硬盘,不仅闪存技术更为精进,性能也得到了进一步的跃升。WD Blue SN570 NVMe SSD的包装外
Top