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