美文网首页
CopyOnWriteArrayList源码分析

CopyOnWriteArrayList源码分析

作者: 哈密朵 | 来源:发表于2019-07-06 21:33 被阅读0次

    如果要并发读写,如果用synchronized和ReentrantLock那会严重阻塞 读-读,读-写,写-写。如果用ReadWriteLock虽然 读-读 ok了,但 读-写 还是阻塞,只要有一个线程写,所有读线程都会阻塞。

    CopyOnWriteArrayList 写时复制用空间换时间,读-读 和 读-写 都不再阻塞。适合多读偶尔写,并且允许短暂的读旧数据,不要求强一致性。

    注意Object[]有volatile,说明Object[]整个数组引用发生写(替换)时,新数组会对所有线程可见,但Object[]的内容发生写的时候,是不保证新写的内容对其余线程可见。

    volatile对其标识的变量或对象引用有效,跟对象内部字段无关。不要想着把对象标识成 volatile 就万事大吉,认为其内部变更能对所有线程可见。

    读时直接读Object[],没有任何要求。

    写时用lock加锁互斥其他线程的写,复制Object[],在新Object[]里写入后,把Object[]整个替换回去。Object[]有volatile保证新数组的引用对之后的读线程可见。

    请注意:lock释放锁时也会将释放锁之前的所有操作,包括写volatile的Object[]操作和任意的写普通共享变量的操作,都刷回主存对其余线程可见。但根据happen-before规则要求其余线程要先获取lock才能完成happen-before链,达到可见性。

    但读操作并没有获取lock,不能续上写线程unlock后的happen-before链,所以不能依靠lock的释放-获取达到可见性,只能通过Object[]自己的volatile达到可见性。理解这点很重要,因为马上要分析else里那行看似多余的写Object[]。

    走到else这里Object[]无需写, 但Object[]写之前可能会有任意的写普通共享变量的操作(虽然这里没有)。刚才说过读线程没有获取lock所以写线程不能通过unlock来传递所有操作的可见性,于是这里冗余的写volatile的Object[]来传递所有操作的可见性,因为读线程必定读volatile的Object[]。

    从java 1.5开始,JSR-133规范要求volatile的写-读达到跟synchronized锁的释放-获取 相同的内存语义。

    volatile写之前的操作不能重排序到写之后,volatile写之前的操作在volatile写的时刻一起刷回主存,对其余线程可见。volatile读之后的操作不能重排序到读之前,volatile读的时刻清空处理器高速缓存,所有的读从主存重新读取,完成可见性。

    synchronized释放锁的内存语义,等同于volatile写。synchronized获取锁的内存语义,等同于volatile读。

    其实RenentrantLock的实现就是对AQS内的volatile int state字段的读写,达到写state(释放lock)之前的所有操作,对别的线程之后的读state(获取lock)可见。然后通过cas写state完成互斥。

    相关文章

      网友评论

          本文标题:CopyOnWriteArrayList源码分析

          本文链接:https://www.haomeiwen.com/subject/aqbmhctx.html