我不停奔跑只为追赶当年被寄予厚望的自己。 我们知道 ArrayList 非线程安全,需要自己加锁或者使用 CopyOnWrite 写时复制. 可并发读 CopyOnWrite 容器,而无需加锁,因为当前容器不会添加任何元素. 下面开始看源码,到底是如何实现写时复制的. 向 COW 里添加元素,是需要加锁的,否则并发写时 copy 出N个副本! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 读时无需加锁,如果读时其它线程正在向ArrayList添加数据,读还是只会读到旧数据,因为写时并不会锁住旧的数组. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 依旧三板斧: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 并非直接对数组元素逐个删除,而先对数组值循环判断,将无需删除的数据放到临时数组,最后临时数组中的数据就是我们不需要删除的数据. CopyOnWrite 并发容器适用于读多写少的并发场景.CopyOnWrite容器有很多优点,但同时也存在问题,开发时候需要注意: 写时,内存里会同时驻存两个对象的内存,旧对象和新写入对象(复制的时候只是复制容器里的引用,只是在写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存).若这些对象占用内存较大,很可能造成频繁GC,应用响应时间也变长. CopyOnWrite容器只能保证数据的
——利文斯顿0 前言
Collections.synchronizedList
包装.
从JDK1.5开始JUC里提供了使用 CopyOnWrite 机制实现的并发容器线程安全的 List – CopyOnWriteArrayList,简称 COW1 CopyOnWrite 设计思想
1.1 基本概念
一般来说就是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器复制出一个新的容器,往新的容器里添加元素,添加完元素之后,再将原容器引用指向新容器.
即一开始大家都在共享同一内容,当有人想修改该内容时,才会真地把内容copy出去形成一个新的内容然后再改,这是一种延时懒惰策略.1.2 设计优点
所以这也是一种读写分离的思想,读写的是不同的容器.2 继承体系
3 属性
4 构造方法
4.1 无参
4.2 有参
5 add(E e)
public
boolean
add(E e) {
final
ReentrantLock lock =
this
.lock;
// 1.加锁
lock.lock();
try
{
// 得到原数组
Object[] elements = getArray();
int
len = elements.length;
// 2.复制出新数组,加一是因为要添加yi'ge'yuan's
Object[] newElements = Arrays.copyOf(elements, len +
1
);
// 把新元素添加到新数组里,直接放在数组尾部
newElements[len] = e;
// 把原数组引用指向新数组
setArray(newElements);
return
true
;
}
finally
{
// finally 里面释放锁,保证即使 try 发生了异常,仍然能够释放锁
lock.unlock();
}
}
getArray
setArray
都加锁,为什么还需要拷贝数组,而不直接在原数组修改?
6 get
get(int index)
get(Object[] a, int index)
7 remove
7.1 指定索引删除
public
E remove(
int
index) {
final
ReentrantLock lock =
this
.lock;
// 加锁
lock.lock();
try
{
Object[] elements = getArray();
int
len = elements.length;
// 先得到旧值
E oldValue = get(elements, index);
int
numMoved = len - index -
1
;
// 如果要删除的数据正好是数组的尾部,直接删除
if
(numMoved ==
0
)
setArray(Arrays.copyOf(elements, len -
1
));
else
{
// 若删除的数据在数组中间:
// 1. 设置新数组的长度减一,因为是减少一个元素
// 2. 从 0 拷贝到数组新位置
// 3. 从新位置拷贝到数组尾部
Object[] newElements =
new
Object[len -
1
];
System.arraycopy(elements,
0
, newElements,
0
, index);
System.arraycopy(elements, index +
1
, newElements, index,
numMoved);
setArray(newElements);
}
return
oldValue;
}
finally
{
lock.unlock();
}
}
7.2 批量删除
public
boolean
removeAll(Collection<?> c) {
if
(c ==
null
)
throw
new
NullPointerException();
final
ReentrantLock lock =
this
.lock;
lock.lock();
try
{
Object[] elements = getArray();
int
len = elements.length;
if
(len !=
0
) {
// newlen 表新数组的索引位置,新数组中存在不包含在 c 中的元素
int
newlen =
0
;
Object[] temp =
new
Object[len];
// 循环,把不包含在 c 里面的元素,放到新数组中
for
(
int
i =
0
; i < len; ++i) {
Object element = elements[i];
// 不包含在 c 中的元素,从 0 开始放到新数组中
if
(!c.contains(element))
temp[newlen++] = element;
}
// 拷贝新数组,变相的删除了不包含在 c 中的元素
if
(newlen != len) {
setArray(Arrays.copyOf(temp, newlen));
return
true
;
}
}
return
false
;
}
finally
{
lock.unlock();
}
}
8 总结
内存占用问题
针对该问题,可通过压缩容器中元素,减少大对象的内存,或者直接不使用CopyOnWrite容器,而使用其他并发容器,如ConcurrentHashMap。数据一致性问题
最终一致性
,不能保证数据的实时一致性
,请酌情使用.
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算