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

事与愿违:可变类出现了线程安全问题

来源: 责编: 时间:2023-10-27 17:20:52 198观看
导读大家好,我是冰河~~“确实在公司跟着老大能学到很多知识啊,之前确实也不怎么了解线程安全问题和一些解决方案,现在了解了,也终于基于不可变类实现了一个简单的功能,明天找老大帮我看看“,小菜心里想着,脸上露出了满意的微笑。

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

大家好,我是冰河~~CaN28资讯网——每日最新资讯28at.com

“确实在公司跟着老大能学到很多知识啊,之前确实也不怎么了解线程安全问题和一些解决方案,现在了解了,也终于基于不可变类实现了一个简单的功能,明天找老大帮我看看“,小菜心里想着,脸上露出了满意的微笑。CaN28资讯网——每日最新资讯28at.com

一、情景再现

上回说到:小菜在自己实现分配的统计商品详情接口调用次数的功能时,没注意线程安全问题,导致统计出来的结果数据与实际结果偏差较大,通过老王的耐心讲解,知道了背后产生问题的根本原因,也学到了几种并发问题的解决方案。CaN28资讯网——每日最新资讯28at.com

下班后,小菜自己尝试基于不可变类实现一个简单的功能,但是。。。CaN28资讯网——每日最新资讯28at.com

二、事与愿违

第二天,小菜早早来到公司,昨天自己想基于不可变类实现一个简单的功能,经过自己不懈的努力,终于“完成”了自己想象的功能,心里也是比较高兴的。就等着老王来公司后,给老王看看自己实现的功能。CaN28资讯网——每日最新资讯28at.com

正想着,小菜听到了老王说话的声音,原来是老王跟几个同事一起到公司了。看着老王走到了自己的工位上,小菜拿着自己的电脑来到老王身边说:”老大,我昨天学了不少并发问题的解决方案,对不可变类这种方式很感兴趣,回去后自己基于这种方式实现了一个小功能,你帮我看看实现的对吗?“。CaN28资讯网——每日最新资讯28at.com

老王听后说:“我看看,你给我简单说下实现的功能是啥?”。CaN28资讯网——每日最新资讯28at.com

“咱们乘坐高铁,在进站时不是都要通过身份证检票吗,我就想通过不可变类模拟实现一个检票的功能,这个检票功能支持并发访问,也就是同时支持多个人拿着身份证通过检票。CaN28资讯网——每日最新资讯28at.com

在实现上,我想的比较简单,就是通过一个名字和身份证编号来定义一个不可变类,表示一个用户,由这个不可变类支持线程安全。再由一个Map来存储这些用户的信息,当用户通过检票时,更新下用户的信息,最终打印出来。整个过程基于不可变类实现线程安全”。CaN28资讯网——每日最新资讯28at.com

“我还画了一张图”,说着小菜从电脑里打开了自己画的场景需求图,如图4-1所示。CaN28资讯网——每日最新资讯28at.com

图片CaN28资讯网——每日最新资讯28at.com

老王听了后说:“嗯,我大概明白你的需求了,我看看代码实现”。CaN28资讯网——每日最新资讯28at.com

于是小菜便把电脑给了老王,要不说老王是大牛呢?老王只是用他那凌厉的眼扫了一眼,便说道:“这代码有问题”。CaN28资讯网——每日最新资讯28at.com

“啊”,小菜当时就有点懵,“这,我觉得没问题呀”。。。CaN28资讯网——每日最新资讯28at.com

三、分析代码

“那我们就结合代码来分析下原因吧”,老王说着,便让小菜看代码。“首先是这个User用户类”。CaN28资讯网——每日最新资讯28at.com

User类的源码详见:concurrent-design-patterns-immutable工程下的io.binghe.concurrent.design.demo.wrong.User。CaN28资讯网——每日最新资讯28at.com

public class User {    private String name;    private Long idCard;    public void set(String name, Long idCard){        this.name = name;        this.idCard = idCard;    }    @Override    public String toString() {        return "User{" +                "name='" + name + '/'' +                ", idCard=" + idCard +                '}';    }}

“这个User类就是有问题的,你知道什么是不可变类吗?”,老王问小菜。CaN28资讯网——每日最新资讯28at.com

小菜说:“知道,就是一个类一经创建,就不会发生变化的类,就叫做不可变类”。CaN28资讯网——每日最新资讯28at.com

“对,概念记得倒是挺清楚的,但是这个User类不是一个不可变类呀,我们根据不可变类的定义分析下这个User类为什么不是一个不可变类”,老王巴拉巴拉的说了起来。总体上,老王针对User类为什么不是不可变类,总结了如下几点:CaN28资讯网——每日最新资讯28at.com

  • 用户类没有被final修饰,可以有其他类继承User类,一旦有子类继承,就可能改变User类的状态。
  • User类里的成员变量没有被final修饰,可能会发生变化。
  • User类中提供了修改成员变量的方法。成员变量可能发生变化。
  • User类的set()方法也不是原子的,存在线程安全问题,多个线程同时访问可能会存在并发问题。

“明白了吗?”,老王问小菜。CaN28资讯网——每日最新资讯28at.com

“明白了”,小菜回答道,“其实我总觉得哪里有点怪,就是说不上来,我以为我写的是对的”,小菜不好意思的笑了笑。CaN28资讯网——每日最新资讯28at.com

“那我们再来看看你写的这个TicketCheck类”,老王继续说道,说着打开了小菜写的TicketCheck类的代码片段。CaN28资讯网——每日最新资讯28at.com

TicketCheck类的源码详见:concurrent-design-patterns-immutable工程下的io.binghe.concurrent.design.demo.wrong.TicketCheck。CaN28资讯网——每日最新资讯28at.com

public class TicketCheck {    private Map<String, User> userMap = new ConcurrentHashMap<>();    public void updateUser(String userKey, String userName, Long idCard){        User user = userMap.get(userKey);        user.set(userName, idCard);        System.out.println(Thread.currentThread().getName() + "--当前检票的用户是:" + user.toString());        userMap.put(userKey, user);    }    public User getUser(String userKey){        return userMap.get(userKey);    }}

“这个类也相对比较简单”,老王继续说道:“但是这类会改变User对象内部的状态,User类本身就不是一个不可变类,加上TicketCheck类也确实通过用户类的set()方法改变了用户类的状态,如果多个线程访问了同一个userKey中的User对象,就可能会存在线程安全问题,所以整体不能基于不可变类保证线程安全”。CaN28资讯网——每日最新资讯28at.com

此时的小菜有点一脸懵逼,眉头拧成了一个麻花。CaN28资讯网——每日最新资讯28at.com

老王看了一眼小菜,说到:“刚才我说的听明白了吗?”。CaN28资讯网——每日最新资讯28at.com

“有点听不明白了”,我写的TicketCheck类,其实并不是要修改User类,而是为User类设置userName和idCard属性,实际并不会修改User类的信息,只是记录检票的用户,并且打印用户的信息,不太明白为啥不能基于不可变类保证线程安全“。CaN28资讯网——每日最新资讯28at.com

“这样吧,我给你画张图分析一下”,老王说道。CaN28资讯网——每日最新资讯28at.com

于是,老王打开了电脑的画图工具。。。CaN28资讯网——每日最新资讯28at.com

四、画图分析

要不说老王这人就是牛,对其他同事也特别好呢,不一会,就画出了一张分析图,如图4-2所示。CaN28资讯网——每日最新资讯28at.com

图片CaN28资讯网——每日最新资讯28at.com

“我们就基于你写的User类进行讲解,看这张图”,老王继续说到,“假设现在user对象的name为张三,idCard为1001,线程1获取到用户信息时,此时的name为张三,idCard为1001,线程1调用user对象的set()方法来修改用户的信息。我们来看user的set()方法”,老王又打开了User类的代码,重点让小菜看set()方法的代码。CaN28资讯网——每日最新资讯28at.com

public void set(String name, Long idCard){    this.name = name;    this.idCard = idCard;}

“在set()方法中,会分别修改user的name字段和idCard的值,这个过程并不是原子操作,线程1在执行set()方法时,在更新完name字段的值时,如果此时恰好发生了线程切换,线程2获取用户信息时,获取到的用户的name字段为张三,idCard字段为1001。这时,线程2获取到的数据是错乱的,线程2获取到的用户name字段为李四,idCard却是张三的身份证编号,用户数据发生了错乱的现象,出现了线程安全问题”。CaN28资讯网——每日最新资讯28at.com

“这么说能听明白吗?”,老王又问小菜。CaN28资讯网——每日最新资讯28at.com

“嗯,这次明白了”,小菜回复到。CaN28资讯网——每日最新资讯28at.com

“那我们继续讲讲怎么写不可变类的代码吧”,老王接着说。CaN28资讯网——每日最新资讯28at.com

“好的”。CaN28资讯网——每日最新资讯28at.com

正当老王准备讲如何写不可变类的代码时,此时听到一个熟悉的声音,“王工,有个新的需求要和技术这边一起讨论下可行性,你参与一下呀?”,老王抬头一看,原来是产品经理,边说边往这边走,于是回了句:“好的”。CaN28资讯网——每日最新资讯28at.com

老王转过有来对小菜说:“那我们今天就到这儿,你先结合今天分析的内容,思考下怎么写不可变的类,有时间咱们再接着聊,我去开会”。(老王真特么是个大好人)。CaN28资讯网——每日最新资讯28at.com

“好的”,小菜接着说。CaN28资讯网——每日最新资讯28at.com

于是,老王拿着电脑跟产品经理去开会了,小菜回到了自己的工位,开始了一天的工作。。。CaN28资讯网——每日最新资讯28at.com

五、本章总结

本章,以场景故事的形式描述了不可变类存在的线程安全问题,以及对不可变类存在的线程安全问题进行了分析。CaN28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-15436-0.html事与愿违:可变类出现了线程安全问题

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

上一篇: .Net JIT骚操逆向最新版Dngurad HVM

下一篇: 在Kubernetes中实现gRPC流量负载均衡

标签:
  • 热门焦点
  • 6月安卓手机好评榜:魅族20 Pro蝉联冠军

    6月安卓手机好评榜:魅族20 Pro蝉联冠军

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年6月1日至6月30日,仅限国内市场。第一名:魅族20 Pro好评率:95%5月份的时候魅族20 Pro就是
  • 5月安卓手机好评榜:魅族20 Pro夺冠

    5月安卓手机好评榜:魅族20 Pro夺冠

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年5月1日至5月31日,仅限国内市场。第一名:魅族20 Pro好评率:97.50%不得不感慨魅族老品牌还
  • 0糖0卡0脂 旭日森林仙草乌龙茶优惠:15瓶到手29元

    0糖0卡0脂 旭日森林仙草乌龙茶优惠:15瓶到手29元

    旭日森林无糖仙草乌龙茶510ml*15瓶平时要卖为79.9元,今日下单领取50元优惠券,到手价为29.9元。产品规格:0糖0卡0脂,添加草本仙草汁,清凉爽口,富含茶多酚,保留
  • K8S | Service服务发现

    K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • SpringBoot中使用Cache提升接口性能详解

    SpringBoot中使用Cache提升接口性能详解

    环境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架从 3.1 开始,对 Spring 应用程序提供了透明式添加缓存的支持。和事务支持一样,抽象缓存允许一致地使用各
  • 如何正确使用:Has和:Nth-Last-Child

    如何正确使用:Has和:Nth-Last-Child

    我们可以用CSS检查,以了解一组元素的数量是否小于或等于一个数字。例如,一个拥有三个或更多子项的grid。你可能会想,为什么需要这样做呢?在某些情况下,一个组件或一个布局可能会
  • 一篇聊聊Go错误封装机制

    一篇聊聊Go错误封装机制

    %w 是用于错误包装(Error Wrapping)的格式化动词。它是用于 fmt.Errorf 和 fmt.Sprintf 函数中的一个特殊格式化动词,用于将一个错误(或其他可打印的值)包装在一个新的错误中。使
  • 腾讯盖楼,字节拆墙

    腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之&ldquo;想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!&rdquo;曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • OPPO K11样张首曝:千元机影像“卷”得真不错!

    OPPO K11样张首曝:千元机影像“卷”得真不错!

    一直以来,OPPO K系列机型都保持着较为均衡的产品体验,历来都是2K价位的明星机型,去年推出的OPPO K10和OPPO K10 Pro两款机型凭借各自的出色配置,堪称有
Top