J.U.C — Collections — CopyOnWrite****

背景

1.J.U.C 中主要由CopyOnWriteArrayList和CopyOnWriteArraySet(后者底层基于前者实现,add时调用前者的addIfAbsent方法保证不重复)。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。

  2. CopyOnWriteArrayList,因何而存在?

   ArrayList的一个线程安全的变体,其所有可变操作(addset 等)都是通过对底层数组进行一次新的复制来实现的,代价昂贵。CopyOnWriteArrayList,是因”并发”而生。


一. CopyOnWriteArrayList使用

  • 【场景一】对于ArrayList,使用直接方式,一边遍历,一边删除,会报错。
// 删除/修改元素
 for(String item : list){
     list.remove(item);
 }
/* 原因:foreach语法是通过Iterator来实现的,当遍历这个List的时候,会生成一个ArrayList.Itr对象,这个私有内部类实现了Iterator接口,也就是说上面这段代码与下面这段代码效果一样:
  Iterator it = list.iterator() ;
  while(it.hasNext()){
    String temp = it.next() ;
    list.remove(temp) ; 
  }
在ArrayList中有一变量记录的当前这个ArrayList被修改的次数,每当调用add/remove方法就会把该参数的值加一,当生成Iterator对象时该对象会记录当前状态ArrayList的修改次数,然后在每次调用it.next()时就会判断当前ArrayList修改次数是否和它记录的相同,如果不同就抛出异常。例如上面等效后的代码,当执行 it = list.iterator() ;时 it对象记录在执行这句代码之前list对象的修改次数,当第一次执行it.next()语句时不会发生异常,因为这时list还没有被修改,但是当第二次执行it.next()时list已经通过list.remove(temp)代码修改了其内部的修改次数变量,所以导致it对象记录的修改次数和list的修改次数不同,所以就抛出了异常。
只需记住的准则是: 使用Iterator遍历集合时是不能修改集合的

解决办法一:使用迭代器,一边遍历,一边删除,不会报错。
// 删除/修改元素 
Iterator<String> it = list.iterator(); 
while(it.hasNext()){ 
    String ele = it.next(); 
    it.remove(); 
} 
使用Iterator的remove方法时不会对集合是否被更改进行判断,所以上面不会出现ModifyException。
 
解决办法二:使用CopyOnWriteArrayList,直接方式,一边遍历,一会删除,不会报错。
for(String item : list){ 
    list.remove(item); 
} 
 使用CopyOnWriteArrayList的remove方法时,该方法会先将List复制一份,对副本进行修改,然后把以前的List引用重新指向副本,所以不会出现异常。


  • 【场景二】对于ArrayList,使用迭代器,一边遍历,一边add,会报错。
Iterator<String> it = list.iterator(); 
while(it.hasNext()){ 
    String str = it.next(); 
    String tem = str + "..."; 
    list.add(tem); 
} 
原因:因为list.add会修改其内部存储的list修改次数变量,导致it内部记录的list修改次数和list当前状态的修改次数不同,所以产生异常。
 解决办法一:改用CopyOnWriteArrayList,直接方式,一边遍历,一边add,不会报错。
for(String item : list){ 
    String tem = item + "..."; 
    list.add(tem); 
} 
 
使用CopyOnWriteArrayList的add方法时,该方法会先将List复制一份,对副本进行修改,然后把以前的List引用重新指向副本,所以不会出现异常,
直接遍历与使用Iterator的效果是一样的。
 解决办法二:改用CopyOnWriteArrayList,一边遍历,一边add,不会报错。
        Iterator<String> it = list.iterator(); 
        while(it.hasNext()){ 
            String str = it.next(); 
            String tem = str + "..."; 
            list.add(tem); 
        }

  • 【场景三】对于CopyOnWriteArrayList,迭代器,不能remove。
Iterator<String> it = list.iterator(); 
while(it.hasNext()){ 
    String str = it.next(); 
    String tem = str + "..."; 
    it.remove(); 
} 
CopyOnWriteArrayList实现的remove方法中直接抛出异常,说明不支持该操作,至于为什么这样做还待以后慢慢体会。

二. CopyOnWriteArrayList 原理

  1.      储存容器
         
  1.     初始化
          
  1.     查询方法(indexOf, size, isEmpty, get)都是在原数组上操作
          
  1.     修改方法(set, remove, add)会拷贝一份数组,在修改之后,将原来的数组引用指向新的数组
          

三. CopyOnWriteArrayList适用于读多写少的场景

四. CopyOnWriteArrayList缺点

  • 内存消耗:因为在修改得时候,存在旧数组和新数组占用内存
  • 数据实时一致性:在修改期间读到的数据可能是脏数据



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值