CopyOnWriteArrayList中的set方法随记

最近一直在看并发编程网,这篇文章先记录下这个地方的理解。

上下文环境移步CopyOnWriteArrayList类set方法疑惑?

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** The array, accessed only via getArray/setArray. */  
  2. private volatile transient Object[] array;  
  3.   
  4. /** 
  5.  * Replaces the element at the specified position in this list with the 
  6.  * specified element. 
  7.  * 
  8.  * @throws IndexOutOfBoundsException {@inheritDoc} 
  9.  */  
  10. public E set(int index, E element) {  
  11.     final ReentrantLock lock = this.lock;  
  12.     lock.lock();  
  13.     try {  
  14.         Object[] elements = getArray();  
  15.         E oldValue = get(elements, index);  
  16.   
  17.         if (oldValue != element) {  
  18.             int len = elements.length;  
  19.             Object[] newElements = Arrays.copyOf(elements, len);  
  20.             newElements[index] = element;  
  21.             setArray(newElements);  
  22.         } else {  
  23.             // Not quite a no-op; ensures volatile write semantics  
  24.             setArray(elements);  
  25.         }  
  26.         return oldValue;  
  27.     } finally {  
  28.         lock.unlock();  
  29.     }  
  30. }  
  31.   
  32. /** 
  33.  * Sets the array. 
  34.  */  
  35. final void setArray(Object[] a) {  
  36.     array = a;  
  37. }  
  38.   
  39. /** 
  40.  * Gets the array.  Non-private so as to also be accessible 
  41.  * from CopyOnWriteArraySet class. 
  42.  */  
  43. final Object[] getArray() {  
  44.     return array;  
  45. }  

可以看见set方法中else判断中看起来似乎有个空操作setArray,将读取的数组又塞回来赋值。

好像这个地方有没有不管对于单线程还是多线程对elements的结果或者读取的element都不造成影响,看了很久才理解一点那么个意思。

这里注意注释中说明,保证volatile写语义,要理解这句话得先理解java中的指令重排和happens before原则。

并发编程网也有很好的几篇文章解释,详细内容移步happens-before俗解有关“双重检查锁定失效”的说明

这里调用setArray的话主要保证了对于下列情况,set方法happens before于get方法。

线程1线程2
1a = 1;
2list.set(1,"t");
1list.get(0);
2int b = a;
假如set方法中没有那么一句话,而list[1]原来就是"t",那么这里可能由于指令重排,导致list.get(0)获得是t,而b得到的却不是1.


然后是这里为什么要将引用指向clone的一个新对象?

对于在set之前得到指向老对象引用的不进行干扰,包括getArray和iterator等,而且CopyOnWriteArrayList在大多为读操作时才使用,写操作性能较差。发布一个不可变对象,是不需要进一步的同步操作,这是CopyOnWrite的核心思路。


上面双重检查锁定失效的问题这里也提一下,主要问题是有可能对象赋值给引用的指令和对象初始化赋值的指令重排序,导致getInstance拿到对象时查看到未成功初始化完的对象属性。如果对单例引用加上volatile修饰符,会保证完成引用赋值时对象构造函数的赋值完成。

延迟初始化单例的方式还有一种更好的思路,采用占位类方式来实现,当使用类的时候才会初始化,而且不会出现并发问题。

public class ResourceFactory{
    private static class ResourceHolder{
        public static Resource resource = new Resource();
    }

    public static Resource getResource(){
      return ResourceHolder.resource;
    }
}


感觉还有些细节需要再深入了解下,如果有理解不对的地方麻烦指出,多谢。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值