首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

望CopyOnWriteArrayList源代码之后

2013-02-27 
看CopyOnWriteArrayList源代码之后? ? ? ? 最近写代码的时候傲然F3点进去看了一下源代码,看了之后有几个问

看CopyOnWriteArrayList源代码之后

? ? ? ? 最近写代码的时候傲然F3点进去看了一下源代码,看了之后有几个问题,然后找资料解决了,记录一下。?

? ? ? ?CopyOnWriteArrayList在写的时候复制整个数组,这样操作其实比较耗时,所以这个List适合写少读多的并发场景,此时有很好的性能。?读操作(get(),indexOf(),isEmpty(),contains())不加任何锁,而写操作(set(),add(),remove())通过Arrays.copyOf()操作拷贝当前底层数据结构(array),在其上面做完增删改等操作,再将新的数组置为底层数据结构,同时为了避免并发增删改,?CopyOnWriteList在这些写操作上通过一个ReetranLock进行并发控制。

????????数据结构:Object数组(含有get和set方法)

????????关于读和写:

????????链表中单个元素的get操作,直接返回数组中对于的数据。

123public?E get(int?index) {???????return?(E)(getArray()[index]);???}

????????链表中元素的设置set操作,先copy一份,然后添加元素,之后再调用setArray来重新赋值。

1234567891011121314151617181920212223public?E set(int?index, E element) {????//加锁操作????final?ReentrantLock lock =?this.lock;????lock.lock();????try?{????????Object[] elements = getArray();????????//获取原先数据????????Object oldValue = 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?(E)oldValue;????}?finally?{????????lock.unlock();????}}

1、在set中有个疑问,就是为啥在新老对象相同的时候,还调用了一下setArray函数呢?

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

从注释来看,是确保volatile的写的语义。至于为啥,看了邮件列表中,应该是为了确保happends-before特性。

望CopyOnWriteArrayList源代码之后

http://blog.csdn.net/fg2006/article/details/6397142??

2、看了一下,貌似这个方法调用的比较多System.arraycopy,Arrays.copyof?里面也是调用的这个方法,这个方法是干啥的?

这个在System中是一个native方法,负责数组拷贝,参数简单明了。

123456789/* @param????? src????? the source array.?* @param????? srcPos?? starting position in the source array.?* @param????? dest???? the destination array.?* @param????? destPos? starting position in the destination data.?* @param????? length?? the number of array elements to be copied.?*/public?static?native?void?arraycopy(Object src,??int??srcPos,????????????????????????????????????Object dest,?int?destPos,????????????????????????????????????int?length);

写了个程序对比了一下使用System和自己循环进行数据拷贝的效率问题,还是原生的给力啊。

1234567891011121314151617181920212223242526272829303132333435import?java.util.Arrays;import?java.util.List;import?java.util.concurrent.CopyOnWriteArrayList;public?class?ListTest {????public?static?void?main(String[] args) {????????int?size =?1000;????????//每次copy运行的次数????????int?runTime =?100000;????????String[] src =? genString(size);????????String[] destSysCopy =?new?String[size];????????String[] destSelfloop =?new?String[size];????????/**通过System.arrayCopy来复制*/????????long?s1 = System.currentTimeMillis();????????for(int?i=0;i<runTime;i++){????????????System.arraycopy(src,?0, destSysCopy,?0, src.length);????????}????????System.out.println("arrayCopy:"+(System.currentTimeMillis()-s1)+",size="+destSysCopy.length);????????/**通过循环来完成复制*/????????long?s2 = System.currentTimeMillis();????????for(int?i=0;i<runTime;i++){????????????for(int?j=0;j<src.length;j++){????????????????destSelfloop[j] = src[j];????????????}????????}????????System.out.println("selfLoop:"+(System.currentTimeMillis()-s2)+",size="+destSelfloop.length);????}????/**初始化算是数组*/????private?static?String[] genString(int?size){????????String[] a =?new?String[size];????????for(int?i=0;i<size;i++){????????????a[i] =?"iamzhongyong+"+i;????????}????????return?a;????}}12arrayCopy:78,size=1000selfLoop:453,size=1000

3、关于迭代器

????????CopyOnWriteList所实现的迭代器其数据也是底层数组镜像,所以在CopyOnWriteList进行interator,同时并发增删改CopyOnWriteList里的数据实不会抛“ConcurrentModificationException”,当然在迭代器上做remove,add,set也是无效的(抛UnsupportedOperationExcetion),因为迭代器上的数据只是当前List的数据数组的一个拷贝而已。

123public?Iterator<E> iterator() {??????return?new?COWIterator<E>(getArray(),?0);??}12345678910public?void?remove() {????????????throw?new?UnsupportedOperationException();????????}public?void?set(E e) {????????????throw?new?UnsupportedOperationException();????????}???public?void?add(E e) {????????????throw?new?UnsupportedOperationException();????????}

?

4、为啥数据对象加了volatile,不加有啥问题?

private?volatile?transient?Object[]?array;

????????Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。(http://www.infoq.com/cn/articles/ftf-java-volatile??)

????????如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。

????????Volatile变量修饰符如果使用恰当的话,它比synchronized的使用和执行成本会更低,因为它不会引起线程上下文的切换和调度。

????????锁提供了两种主要特性:互斥(mutual?exclusion)?和可见性(visibility)。

????????互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。

????????可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的?——?如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。

????????Volatile?变量具有?synchronized?的可见性特性,但是不具备原子特性。这就是说线程能够自动发现?volatile?变量的最新值。

????????Volatile?变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。

????????至此能够清楚了,array的值在写的时候,通过synchronized来保证原子性,但是此时还有别的线程可能用到了array这个对象,例如在读的时候,这时候通过volatile能够保证可见性。?????

5、CopytOnWriteArrayList中写方法使用RentrantLock来实现同步,为啥不用synchronized呢?

这个可能是历史原因,在jdk6之前,synchronized在多线程情况下吞吐量下降和明显,而Lock的话会好很多,所以并发包中采用了Lock的机制,后来JDK6对于synchronized锁做了优化,两者持平,所以正在synchronized能够满足需求的情况下,优先考虑使用这个,毕竟非常方便吗。

重入锁:指的是同一个线程多次试图获取它所占有的锁,请求会成功。当释放锁的时候,直到重入次数清零,锁才释放完毕。

搞个例子对比一下现在的情况,例子参照:http://www.blogjava.net/killme2008/archive/2007/09/14/145195.html?

Counter接口:

1234public?interface?Counter {????public?long?getValue();????public?void?increment();}

三种情况下的实现:

1234567891011public?class?NoLockCounter?implements?Counter {????private?long?count =?0;????@Override????public?long?getValue() {????????return?count;????}????@Override????public?void?increment() {????????count++;????}}1234567891011public?class?SynchronizeCounter?implements?Counter {????private?long?count =?0;????@Override????public?long?getValue() {????????return?count;????}????@Override????public?synchronized?void?increment() {????????count++;????}}12345678910111213141516171819public?class?ReentranLockCounter?implements?Counter {????private?volatile?long?count =?0;????private?Lock lock =?new?ReentrantLock();???????????????????????????????????????????????????@Override????public?long?getValue() {????????return?count;????}????@Override????public?void?increment() {????????lock.lock();????????try{????????????count++;????????}finally{????????????//切记在finally中释放锁,synchronize的话JVM会自动释放锁????????????lock.unlock();????????}????}}

单线程情况下的测试:

1234567891011121314151617181920212223242526public?class?LockTest {????public?static?void?main(String[] args) {????????//单线程情况下基本没啥插件,synchronize略强一点,无锁最好,这个不用说呵呵????????int?runTime =?100000000;????????Counter c1 =?new?SynchronizeCounter();????????Counter c2 =?new?ReentranLockCounter();????????Counter c3 =?new?NoLockCounter();????????// 单线程情况下测试????????long?s1 = System.currentTimeMillis();????????for(int?i=0;i<runTime;i++){????????????c1.increment();????????}????????System.out.println("synchronize锁,C1:"+c1.getValue()+",用时:"+(System.currentTimeMillis()-s1));????????long?s2 = System.currentTimeMillis();????????for(int?i=0;i<runTime;i++){????????????c2.increment();????????}????????System.out.println("ReentranLock锁,C2:"+c2.getValue()+",用时:"+(System.currentTimeMillis()-s2));??????????????????????????????????????????????????????long?s3 = System.currentTimeMillis();????????for(int?i=0;i<runTime;i++){????????????c3.increment();????????}????????System.out.println("无锁,C3:"+c3.getValue()+",用时:"+(System.currentTimeMillis()-s3));????}}

多线程情况测试,原生锁和重入锁差别不大,无锁有现成安全问题:

1234567891011121314151617181920212223242526272829303132333435363738import?java.util.concurrent.CountDownLatch;?????????????????????????????????????????????public?class?BenchmarkTest {????private?Counter counter;?????????????????????????????????????????????????public?static?void?main(String[] args)??throws?Exception{????????Counter c1 =?new?SynchronizeCounter();????????Counter c2 =?new?ReentranLockCounter();????????Counter c3 =?new?NoLockCounter();????????//这里和参照例子不同,我用来CountDownLatch来做现成同步????????CountDownLatch latch1 =?new?CountDownLatch(1000);????????CountDownLatch latch2 =?new?CountDownLatch(1000);????????CountDownLatch latch3 =?new?CountDownLatch(1000);?????????????????????????????????????????????????????//100线程,每个线程加100,增把数据增加到10000????????long?s1 = System.currentTimeMillis();????????for(int?i=0;i<1000;i++){????????????new?TestThread(c1,latch1).start();????????}????????latch1.await();????????System.out.println("SynchronizeCounter的结果是:"+c1.getValue()+",用时:"+(System.currentTimeMillis()-s1));?????????????????????????????????????????????????????long?s2 = System.currentTimeMillis();????????for(int?i=0;i<1000;i++){????????????new?TestThread(c2,latch2).start();????????}????????latch2.await();????????System.out.println("ReentranLockCounter结果是:"+c2.getValue()+",用时:"+(System.currentTimeMillis()-s2));?????????????????????????????????????????????????????long?s3 = System.currentTimeMillis();????????for(int?i=0;i<1000;i++){????????????new?TestThread(c3,latch3).start();????????}????????latch3.await();????????System.out.println("无锁的结果是:"+c3.getValue()+",用时:"+(System.currentTimeMillis()-s3));?????????????????????????????????????????????????}}123SynchronizeCounter的结果是:100000,用时:125ReentranLockCounter的结果是:100000,用时:125无锁的结果是:99999,用时:109

?

?

?

?

?

?

热点排行