范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Java的各种集合为什么不安全(ListSetMap)以及代替方案

  我们已经知道多线程下会有各种不安全的问题,都知道并发的基本解决方案,这里对出现错误的情况进行一个实际模拟,以此能够联想到具体的生产环境中。一、List 的不安全1.1 问题
  看一段代码:public static void main(String[] args) {     ArrayList list = new ArrayList<>();      for (int i = 0; i < 3; i++){         new Thread(()->{             list.add(UUID.randomUUID().toString().substring(0,8));             System.out.println(list);         },String.valueOf(i)).start();     } }
  过程很简单,只有 3 个线程而已,对同一个 list 进行 add 的写操作,并随后进行输出的读操作。
  输出结果,多执行几次,惊喜多多。
  那么,情况不严重的时候,这里显然还正常运行结束了,只是导致了还没来得及写的时候,就已经读出了数据。
  如果把线程数增加试试,可能还会看到这样的奇观:
  报错了:重点异常:java.util.ConcurrentModificationException,翻译过来就是并发修改异常。1.2 产生原因
  普通的 ArrayList 集合里面没有任何特殊处理,在多线程情况下,他们可以共同进行访问。
  那么在多线程同时操作的时候,按照操作的情况就有这几种:各个线程都读。不影响,前提是只有读;各个线程都写。会出现问题,这里的点有两种情况:值覆盖问题,因为 ArrayList 的底层数组,写入值的时候要先计算到一个下标位置,然后给对应的位置去赋值,多线程就会出现值覆盖的问题;空指针异常,因为 ArrayList 的底层数组,写入值在数组满的时候需要扩容,在扩容还没完成的时候,新的下标却已经计算出来并且要去插入,那么就会出现空指针异常。有的读有的写。那么显然对于多个线程来说,2 里面各个线程写的情况对应的问题就会出现。除此之外:如果多线程有的读有的写,对于 ArrayList 底层,某些情况下,对象是不允许进行修改的,如果修改了,后面调用某些方法时,就会检测到,然后就直接抛出ConcurrentModificationException。具体一下,因为源码里,写操作对集合修改是写,而next、remove等 Itr 的遍历读操作的时候会通过当前集合的修改次数与 Itr 对象创建时记录的次数校验集合是否被修改,如果修改了,不一致就说明正读的时候还有别的线程在改,就会抛出异常。JDK作者说了,会抛这个异常的都叫fail-fast iterator。
  第 3 种情况就是对应了我们上面的代码在线程多起来的情况,因为输出 list 的时候需要遍历的读,而此时还有别的线程在进行 add 的修改操作。1.3 解决方法
  注意:当然不能自己加锁,因为集合类已经再演变过程有线程安全的替代品,自己的代码加锁的粒度已经在集合的外层再加一层了,粒度太大。同样能够完成 ArrayList 功能的,可以使用 Vector,查看源码就会发现,Vector 的基本结构是一个叫 elementData 的 Object 类型的数组,和 ArrayList 类似,但是对应的操作方法,基本都加上了 synchronized 关键字,因此它是线程安全的集合。数据量小的时候,使用 Collections.synchronizedList(new ArrayList())这种方式,来包裹这个集合,跟 Collections 里面 synchronizedMap包裹hashmap 是一样的,更多的,还有:
  显然能传入参数的这些基本集合类都是线程不安全的。第三种就是,直接使用 juc 包里面的,CopyOnWriteArrayList() 类,这个类就是并发包给我们提供的线程安全的列表类。1.4里介绍了这个集合。1.4 CopyOnWriteArrayList
  对于 CopyOnWriteArrayList 类,名字上就可以听的出来, 写时复制 的列表。
  首先,按照前面的我们的分析,只要涉及了写的操作,和读或者写搭配的多线程情况,就会出现问题,那么多线程同时读却不会出现问题,因此相比较于直接都加上 synchronized 的方式,他的思想就是:读写分离。这个思想在数据库对于高并发的架构层面也有一样的设计。
  这样一来,对于这个 List 集合来说,分为不同操作的保证线程安全的策略,就能够保证更好的性能。
  写的方法,我们首先可以看 add 方法源码:
  步骤很清楚,如果有了写操作,需要加锁:加锁获取到当前的集合数组;计算长度;调用 Arrays.copyOf 方法进行添加操作,每次只添加一个元素进去;修改引用,更新最新的集合;return true。解锁
  其中的 lock 在源码里就是一个:
  可以看到是一个普通的 Object。
  那么加锁的时候就用 synchronized 对 Object 进行加锁,没有采用 juc 的 ReetrantLock,注释li也写了,偏向于使用内置的 monitor 也就是 synchronized 底层 monitor 锁,这一点也充分说明了 synchronized 的性能更新使得源码作者使用它。
  这个方法是处理最直接的,其他对应的写操作:remove、set等等也是一样的基础流程。
  我们再来看看读操作 get 方法:
  二、HashSet 的不安全
  2.1 问题及原因
  我们还是用 List 一样的测试代码;public class TestSet {     public static void main(String[] args) {         HashSet set = new HashSet<>();         for (int i = 0; i < 100; i++){             new Thread(()->{                 set.add(UUID.randomUUID().toString().substring(0,8));                 System.out.println(set);             },String.valueOf(i)).start();         }     } }
  就会看到一样的错误:
  2.2 出现问题的原因
  其实从出现 ConcurrentModificationException 异常来看,我们可以猜测是和 List 类似的原因导致的异常。
  可以看到,源码里面,Set 的底层维护的是一个 HashMap 来实现。对于遍历操作来说,都是一样的使用了 fail-fast iterator 迭代器,因此会出现这个异常。
  另外,因为 HashSet 的底层是 HashMap ,本质上,对于每一个 key ,保证唯一,使用了一个 value 为 PRESENT 常量的键值对进行存储。
  put 的过程也是调用 map 的 put 方法。2.3 解决方案List 有对应的 Vector 可用,本来就是线程安全的集合,但是 Set 没有;数据量小的时候,使用 Collections.synchronizedSet(new HashSet<>()) 这种方式,来包裹这个集合,上面我们使用 List 的时候也有类似的方法;同样的,juc包为我们提供了新的线程安全集合 CopyOnWriteArraySet()。2.4 CopyOnWriteArraySet按照前面的思路,List 的对应线程安全集合是在 List 集合的数组基础上进行加锁的相关操作。那么 Set 既然底层是 HashMap,对应的线程安全集合就应该是对 HashMap 的线程安全集合进行加锁,或者说直接用 ConcurrentHashMap 集合来实现 CopyOnWriteArraySet 。但事实上,源码并不是这么做的。
  从名字来看,和 ConcurrentHashMap 也没有什么关系,而是类似 CopyOnWriteArrayList 的命名,说明是读写单独处理,来让他成为线程安全的集合,那为什么是 ArraySet 多一个 array 修饰语呢?
  可以看到,他的思路没有顺延 util 包的 HashSet 的实现思路,而是 直接使用了 CopyOnWriteArrayList 作为底层数据结构 。也就是说没有利用 Map 的键值对映射的特性来保证 set 的唯一性,而是用一个数组为基底的列表来实现。(那显然在去重方面就要做额外的操作了。)
  然后每一个实现的方法都很简单,基本是直接调用了 CopyOnWriteArrayList 的方法:
  我们最担心的可能 产生问题的 remove 和 add 方法,也是使用了 CopyOnWriteArrayList 的方法:
  而保证 set 的不重复性质的关键,显然就在于 CopyOnWriteArrayList 的 addIfAbsent 方法,我们还是点进 CopyOnWriteArrayList 源码看一看这个方法的实现:
  其中的 indexOfRange 方法:
  不安全:获取本来的 set ,是一个数组,以快照形式返回当前的数组;indexOfRange 方法通过遍历查找查找元素出现位置,addIfAbsent方法完成不存在则加入,如果前一个为 false 后一个就不会执行;加锁;current 再次获取一次当前的快照,因为有可能第一次判断的过程有了其他线程的插入或者修改操作,此时已经不像等,就进入分支进行判断是否存在;否则就要加入这个元素,和 CopyOnWriteArrayList 添加元素的最后操作是一样的;解锁。
  总结一下就是,线程安全的 Set 集合完全利用了 CopyOnWriteArrayList 集合的方法,对应的操作也是读写分别处理,写时复制的策略,通过 jvm 层面的锁来保证安全,那么保证不重复的方法就是遍历进行比较。
  这样看来,相比于基于 HashMap 的去重方法,效率肯定会降低,不过如果基于线程安全的 HashMap ,插入操作从hash、比较、到考虑扩容各方面会因为加锁的过程更复杂,而对于一个不重复的 Set 来说,完全没必要,所以应该综合考虑之下采用了 List 为基础,暴力循环去重。三、HashMap 的线程不安全
  关于 HashMap 的相关问题,源码里已经分析过,大体是这样的。
  不安全:普通读写不一致问题;死循环问题;ConcurrentModificationException 异常。
  解决:util包的Hashtable集合线程安全;用 synchronizedMap(new HashMap())包装;使用 juc 包的 ConcurrentHashMap。c 包的 ConcurrentHashMap。
  原文链接:https://www.cnblogs.com/lifegoeson/p/13813509.html

从长春一汽和红旗,看中国汽车行业的昨天今天和明天壹序先看图,下图为长春市2019年上半年GDP数据,排除疫情影响我们就看19年数据,正好这也是国内车市全面进入寒冬之时。2019年上半年15座副省级城市GDP统计数据图中可以看出2最新LPR报价出炉,房贷新规即将实施,我们的房贷利息会降吗?此前央行完善LPR改革,备受市场关注,今天公布LPR最近报价为1年期LPR为4。2,较8月20日首次公布的报价调降5个基点5年期以上LPR为4。85,与上次持平。那么LPR报价对我上云正当时,看雷风桌面云如何助力智慧政务建设从工商人社民政,一个局一个地方,跑断腿式办业务,到一个政务服务中心,就能为办事群众提供一站式服务从人工报税,重复又机械,有时候还会出现错误,甚至要重头来过,到一台电脑就能一键申报,golang2021数据格式(104)示例客户信息管理系统本节带领大家实现一个基于文本界面的客户关系管理软件,该软件可以实现对客户的插入修改和删除,并且可以打印客户信息明细表。软件由一下三个模块组成项目结构如下所示在costumer。goAMDRX6000系列显卡大获成功的秘诀是?最近刚刚发售不久的AMDRX6000系列新显卡可谓是一卡难求,玩家们对此趋之若鹜。是什么让AMDRX6000系列新卡获得了媒体和消费者的一致好评呢?AMD首席游戏架构师FrankA杜卡迪攀爬者TributePRO发布风冷双缸复古味道十足从1971年首次搭载在750GT开始,DUCATI为人津津乐道的气冷双缸引擎设计在今年迎来了问世50周年。而作为气冷双缸设计的传承车款,SCRAMBLER1100车系自然承接了纪念品牌营销竞争秘诀,加进营销告诉你没有哪个品牌不希望在消费市场上做大做强。但是,如果想要品牌在消费市场上真正获得消费者的认可且选择,则需要树立品牌意识,在消费者市场上吸引更多关注或赢得消费者的信任。那么,品牌如何在益而优有机核桃油宝贝吃饭香香的小秘诀我家孩子八个月左右我开始给她添加辅食的,刚添加的辅食时候她什么都不爱吃后来询问了一下儿科医生,才知道原来是油没选对,宝贝长大了肯定也是要多加一点其他营养的,之前她的食谱一点油水都没golang2021数据格式(72)Golangcontainercontainer的结构如下可以看出包含三部分heap,list和ring。下面分别介绍1。heapheap即为堆,是一种常用的数据结构,在源码里面,提供了接口,在实际使用时需要实golang2021数据格式(75)堆的原理完全二叉树的定义性质以及算法见正文,这里补充一点完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率极高,像十分常用的排序算法Dijkstra算法Primgolang2021数据格式(74)list原理分析本文为Goalng全面深入系列中的标准库部分。1。什么是双向链表(引用)和单链表比较,双向链表的元素不但知道自己的下线,还知道自己的上线(越来越像传销组织了)。小煤车开起来,图里面
手机上有哪些高逼格又非常实用的APP?SnapseedSnapseed是Google推出的一款专业的图片编辑工具。它的功能强大到说不清,我挑一些不叫黑科技的功能。它主要分为工具和滤镜功能,它的画笔可以让你修改局部的色温国内游戏限制未成年登入,那steam等外国游戏平台如何处理?无法处理,steam是WTO框架下的小数额数字商品,运营可不受中国大陆监管,但销售的商品要符合中国大陆法律,不过为什么要担心steam?这是电脑平台,不会有家长懒到不想管孩子让孩子学习区块链有前途吗?谢邀,或许有人会问,区块链是技术,是好东西,比特币解决了全球支付问题,也是好东西,那国家为什么要打压比特币呢?我们可以把区块链理解为它是互联网金融社区的结合体。结合传统企业,企业做iPhone12我真的上天了在座各位,你们会如何形容一个人得意忘形时的状态呢?我猜很多人会说你这么牛,咋不上天呢?当然了,这句话也表达了对得意忘形者的不屑,但这句话要是仅从字面上理解,那可就不是一件小事了,而中国的海外云服务火了最近,亚马逊大规模封杀中国电商的消息引起了广泛热议,并在外贸圈子造成了极大的生存危机恐慌。比起被封杀的5万中国卖家而言,幸存下来的商家也并不好受,于当时时间8月16日开始执行的FBMIUI发公告众多旗舰暂停内测全力视频Android12MIUI官方账号近日在社区发布公告,称由于Android大版本升级,小米11Ultra小米11Pro小米11RedmiK40Pro2021年9月6日起暂停发布内测。这几款机型都是小壹米滴答谋划全网提速顺丰再投5亿,推ampampquot员工可持续发展保障ampampquot计划落地五大举措,壹米滴答谋划全网提速近期壹米滴答发力时效提升,并将落地五大举措。包括,9月开始,全网将陆续新增近300条直发线路。从9月1日起,对全网班车进行严格管控,确保所有班车严这类食材可以无限期保存吗?这张图说清楚了!中国工程科技知识中心(以下简称知识中心)是经国家批准建设的国家工程科技领域公益性开放式的知识资源集成和服务平台建设项目,是国家信息化建设的重要组成部分。知识中心建设以满足国家经济科iPhone139月14日上市,这四方面的升级,还是非常有诚意按照国外知名媒体的爆料,苹果的秋季新品发布会将会定档于9月14日,而全新的iPhone13系列将会在9月17日正式上市,这应该也是国内众多果粉最为期待的。同时除了新iPhone之外微信早就盯上聊天记录付费云存储服务,微信会不会搞死自己?这两天微信拟推出聊天记录付费云存储服务的冲上了微博热搜第一位,微信作为国民级别的应用,可见它的每一次改动都会引发全民的关注。作为手机内存的杀手,微信可以说是占据手机内存最大的APP百度网盘阿里云盘腾讯微云,怎么选自己的个人云盘?在这个信息及数据爆炸的年代,手机里的128G256G512G,甚至1T存储,总感觉用着用着就要满了?电脑的512G,1T,2T总感觉还是不够用,在这个万物互联的时代,分享啥也不方便