CopyOnWriteArrayList类set方法疑惑?

在淘宝内网有位同事提了一个很好的问题,大家能否帮忙解答下?

在CopyOnWriteArrayList类的set方法中有一段setArray(elements)代码,实际上这段代码并未对elements做任何改动,实现的volatile语意并不对CopyOnWriteArrayList实例产生任何影响,为什么还是要保留这行语句?见以下代码红体部分:

    /** The array, accessed only via getArray/setArray. */
    private volatile transient Object[] array;

    /**
     * Replaces the element at the specified position in this list with the
     * specified element.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);

            if (oldValue != element) {
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len);
                newElements[index] = element;
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Sets the array.
     */
    final void setArray(Object[] a) {
        array = a;
    }

    /**
     * Gets the array.  Non-private so as to also be accessible
     * from CopyOnWriteArraySet class.
     */
    final Object[] getArray() {
        return array;
    }

这个问题在concurrency-interest邮件列表里也有人讨论:

http://cs.oswego.edu/pipermail/concurrency-interest/2010-February/006886.html

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: CopyOnWriteArrayList类set方法疑惑?


FavoriteLoading添加本文到我的收藏
  • Trackback 关闭
  • 评论 (12)
    • Ticmy
    • 2013/01/16 12:46下午

    我想我可能找到这个问题的答案了,虽然在set方法的的API文档中没有描述它包含的内存语义。但在jdk有个地方描述到了:http://docs.oracle.com/javase/7/docs/api/ 的最下方的几条,摘录过来如下:

    The methods of all classes in java.util.concurrent and its subpackages extend these guarantees to higher-level synchronization. In particular:Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread.

    中文版API中是这样描述的:

    java.util.concurrent 中所有类的方法及其子包扩展了这些对更高级别同步的保证。尤其是: 线程中将一个对象放入任何并发 collection 之前的操作 happen-before 从另一线程中的 collection 访问或移除该元素的后续操作。

    CopyOnWriteArrayList作为java.util.concurrent包中的类之一,当然它也要遵守这个约定。所以才在else里面加了一个 setArray(elements);来保证hb关系。

  1. 目的就是让另外一个线程在get时能马上读到更新后的值。作者在代码上加了一句解释,Not quite a no-op; ensures volatile write semantics,意思是说这行不完全是一个空操作,而是用来确保volatile的写语义。volatile的写语义之一就是插入一个lock前缀的指令,让volatile所修饰字段的缓存行(cache line)无效。一旦cache line无效,getArray时,必须先重新填充cache line,这样处理器就不会使用当前处理器缓存行里的旧值。

    • dys
    • 2013/01/22 9:19上午

    没有明白, 数据本来就没有变化,cache 是否失效 没有关系呀

      • he037
      • 2016/02/17 11:30上午

      感觉就是为了符合java规范吧,及时数据没有变化

    • ryo
    • 2014/08/27 9:51下午

    应该为了能够及时读取到最新修改

    • jenny
    • 2016/02/17 9:48上午

    假如两个两个线程同时获取 E oldValue = get(elements, index);
    第一个线程率先修改了elements;第二发现是相同的没有修改,结果第二次set的时候把修改了的又一次set了一遍

    • 江南烟雨
    • 2016/07/27 3:14下午

    是为了维护happens-before规则,具体可以参考下这个帖子:
    http://stackoverflow.com/questions/28772539/why-setarray-method-call-required-in-copyonwritearraylist

您必须 登陆 后才能发表评论

return top