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

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

01 

/** The array, accessed only via getArray/setArray. */ 

02 

private volatile transient Object[] array; 

03 


04 

/** 

05 

 * Replaces the element at the specified position in this list with the 

06 

 * specified element. 

07 

 * 

08 

 * @throws IndexOutOfBoundsException {@inheritDoc} 

09 

 */ 

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方法的的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关系。


Ticmy
2013/01/16 1:01下午
登录以回复 引用
链接贴错了:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html


Ticmy
2013/01/16 1:25下午
登录以回复 引用
这个hb意义何在?如下例子:a为非volatile的某基本类型变量,coal为CopyOnWriteArrayList对象
t1:
x:a = calValue;
y:coal.set….
———————
t2:
m:coal.get…
n:int tmp = a;

假设存在以上场景,如果能保证只会存在这样的轨迹:x,y,m,n.根据上述java API文档中的约定有hb(y,m),根据线程内的操作相关规定有hb(x,y),hb(m,n),根据hb的传递性读写a变量就有hb(x,n),所以t1对a的写操作对t2中a的读操作可见。如果CopyOnWriteArrayList的set的else里没有setArray(elements)的话,hb(y,m)就不再有了,上述的可见性也就无法保证。


michaelchan
2016/02/18 10:52上午
登录以回复 引用
的确是这样,实际上不是为了保证COW List本身的可见性,而是保证外部的非volatile变量的HB。

参考:
http://stackoverflow.com/questions/28772539/why-setarray-method-call-required-in-copyonwritearraylist