本篇主要介绍juc内部针对数组提供的并发容器CopyOnWriteArrayList,主要的思想是针对增删改等修改操作都将原数组拷贝一份新数组,然后做完修改操作后,通过原子的方式写入到原数组的引用,通知到其他线程。
CopyOnWriteArrayList实现
设计一个线程安全的容器主要针对改(增删改)和查操作,CopyOnWriteArrayList对put操作通过RetrantLock加锁,将数组定义为volatile类型,再通过加锁完成新数组的生成之后,将新数组赋值给volatile数组,通知到其他线程。这里的lock同样我们可以用synchronized关键字或者cas来实现。put操作源码:
1 | /** |
Iterating过程中不允许修改
1 | package com.souche.study.CopyOnWriteArrayList; |
1 | //迭代器浅拷贝,不允许修改,只能查询而且是临时快照 |
通过CAS改写CopyOnWriteArrayList
继承CopyOnWriteArrayList进行方法的重写,重新实现的add方法供参考:
1 | package com.souche.study.CopyOnWriteArrayList; |
CopyOnWriteArrayList设计 vs RetrantWriteReadLock
对比一下CopyOnWriteArrayList设计和RetrantWriteReadLock的实现。
- CopyOnWriteArrayList设计读不加锁,写加锁。RetrantWriteReadLock读和写都加锁。
- CopyOnWriteArrayList设计针对读多写少场景表现优。RetrantWriteReadLock虽然针对读锁做了共享,读多写少依旧阻塞。
总结
感觉CopyOnWriteArrayList的设计还是蛮巧妙的,之前在了解读写锁的这种设计时还是蛮质疑这个设计的实际意义的。类比innodb的MDL表锁(如果一个写操作(修改表结构)获取到写锁前面有一个长事务获取读锁,那么所有的ddl操作(读锁)都有被阻塞),inndb因此设计了更小粒度的行锁。行锁同时设计了写锁,但是取消了读锁,用MVCC多版本并发控制取代,类比CopyOnWriteArrayList保留了写锁,通过线程通信的方式取消了读锁。