在淘宝内网有位同事提了一个很好的问题,大家能否帮忙解答下?
在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