CopyOnWriteArrayList类set方法疑惑?
源:http://ifeve.com/copyonwritearraylist-set/
评:
在淘宝内网有位同事提了一个很好的问题,大家能否帮忙解答下?
在CopyOnWriteArrayList类的set方法中有一段setArray(elements)代码,实际上这段代码并未对elements做任何改动,实现的volatile语意并不对CopyOnWriteArrayList实例产生任何影响,为什么还是要保留这行语句?见以下代码红体部分:
01
/**Thearray,accessedonlyviagetArray/setArray.*/
02
privatevolatiletransientObject[]array;
03
04
/**
05
*Replacestheelementatthespecifiedpositioninthislistwiththe
06
*specifiedelement.
07
*
08
*@throwsIndexOutOfBoundsException{@inheritDoc}
09
*/
10
publicEset(intindex,Eelement){
11
finalReentrantLocklock=this.lock;
12
lock.lock();
13
try{
14
Object[]elements=getArray();
15
EoldValue=get(elements,index);
16
17
if(oldValue!=element){
18
intlen=elements.length;
19
Object[]newElements=Arrays.copyOf(elements,len);
20
newElements[index]=element;
21
setArray(newElements);
22
}else{
23
//Notquiteano-op;ensuresvolatilewritesemantics
24
setArray(elements);
25
}
26
returnoldValue;
27
}finally{
28
lock.unlock();
29
}
30
}
31
32
/**
33
*Setsthearray.
34
*/
35
finalvoidsetArray(Object[]a){
36
array=a;
37
}
38
39
/**
40
*Getsthearray.Non-privatesoastoalsobeaccessible
41
*fromCopyOnWriteArraySetclass.
42
*/
43
finalObject[]getArray(){
44
returnarray;
45
}
===================
我想我可能找到这个问题的答案了,虽然在set方法的的API文档中没有描述它包含的内存语义。但在jdk有个地方描述到了:http://docs.oracle.com/javase/7/docs/api/的最下方的几条,摘录过来如下:
Themethodsofallclassesinjava.util.concurrentanditssubpackagesextendtheseguaranteestohigher-levelsynchronization.Inparticular:Actionsinathreadpriortoplacinganobjectintoanyconcurrentcollectionhappen-beforeactionssubsequenttotheaccessorremovalofthatelementfromthecollectioninanotherthread.
中文版API中是这样描述的:
java.util.concurrent中所有类的方法及其子包扩展了这些对更高级别同步的保证。尤其是:线程中将一个对象放入任何并发collection之前的操作happen-before从另一线程中的collection访问或移除该元素的后续操作。
CopyOnWriteArrayList作为java.util.concurrent包中的类之一,当然它也要遵守这个约定。所以才在else里面加了一个setArray(elements);来保证hb关系。
Ticmy
2013/01/161:01下午
登录以回复引用
链接贴错了:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html
Ticmy
2013/01/161:25下午
登录以回复引用
这个hb意义何在?如下例子:a为非volatile的某基本类型变量,coal为CopyOnWriteArrayList对象
t1:
x:a=calValue;
y:coal.set….
———————
t2:
m:coal.get…
n:inttmp=a;
假设存在以上场景,如果能保证只会存在这样的轨迹:x,y,m,n.根据上述javaAPI文档中的约定有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/1810:52上午
登录以回复引用
的确是这样,实际上不是为了保证COWList本身的可见性,而是保证外部的非volatile变量的HB。
参考:
http://stackoverflow.com/questions/28772539/why-setarray-method-call-required-in-copyonwritearraylist