CopyOnWriteArrayList
CopyOnWriteArrayList允许线程并发访问读操作,这个时候是没有加锁限制的,性能较高。而写操作的时候,则首先将容器复制一份,然后再新的副本上执行写操作,这个时候写操作是上锁的,结束之后再将原容器的引用指向新容器。注意:在上锁执行写操作的过程中,如果有需要读操作,会作用在原容器上。因此上锁的写操作不会影响到并发访问的读操作
构造方法
复制代码
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
30
31
32
33
34
35public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; //互斥锁 final transient ReentrantLock lock = new ReentrantLock(); //底层存储数据数组,只能通过getArray/setArray访问设置,volatile动态数组 private transient volatile Object[] array; final Object[] getArray() { return array; } final void setArray(Object[] a) { array = a; } public CopyOnWriteArrayList() { setArray(new Object[0]); } //传入Collection集合对象,将集合中元素存入CopyOnWriteArrayList public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { elements = c.toArray(); // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); } //传入数组 public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); } }
插入
- 在写入过程中使用了互斥锁,所以同一时间只有一个线程在修改CopyOnWriteArrayList
- 增加元素并不是直接在原数组操作,而是在原数组的拷贝数组上添加元素的,添加完元素再调用setArray方法用新数组代替原始数组
复制代码
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71public boolean add(E e) { //获得互斥锁 final ReentrantLock lock = this.lock; lock.lock(); try { //获取原始数组 Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; //用新的拷贝数组代替原始数组 setArray(newElements); return true; } finally { lock.unlock(); } } public void add(int index, E element) { //互斥锁 final ReentrantLock lock = this.lock; lock.lock(); try { //原始数组 Object[] elements = getArray(); int len = elements.length; //检查index有效性 if (index > len || index < 0) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+len); //拷贝数组 Object[] newElements; //从index到数组末尾要向后移动一位数组元素的个数 int numMoved = len - index; //如果index==length,直接把原数组复制到新数组 if (numMoved == 0) newElements = Arrays.copyOf(elements, len + 1); //否则分成两段复制,原始数组index前面的元素位置一一对应赋值到新数组,原数组index开始的元素复制到 //新数组index+1到length+1,相当于依次后移。空出来的index就是新元素插入的位置 else { newElements = new Object[len + 1]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(elements, index, newElements, index + 1, numMoved); } //插入新元素 newElements[index] = element; setArray(newElements); } finally { lock.unlock(); } } public E set(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); E oldValue = get(elements, index); if (oldValue != element) { int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len); newElements[index] = element; setArray(newElements); } else { setArray(elements); } return oldValue; } finally { lock.unlock(); } }
删除
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public E remove(int index) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; //数组index处要移除的元素 E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) setArray(Arrays.copyOf(elements, len - 1)); else { 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(); } }
查找
复制代码
1
2
3
4
5
6
7
8
9public E get(int index) { return get(getArray(), index); } @SuppressWarnings("unchecked") private E get(Object[] a, int index) { //返回数组index处位置 return (E) a[index]; }
优缺点
- 优点:读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。在遍历传统的List时,若中途有别的线程对其进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其“读写分离”的思想,遍历和修改操作分别作用在不同的List容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常
- 缺点:一是内存占用问题,每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC。二是无法保证实时性,Vector对于读写操作均加锁操作,可以保证读和写的一致性。而CopyOnWriteArrayList由于其实现策略的原因,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据
线程安全的List
- Vector(使用synchronized修饰方法)
- Collections.synchronizedList
复制代码
1
2
3
4
5
6public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list)); }
- CopyOnWriteArrayList
最后
以上就是无辜心情最近收集整理的关于Java集合——CopyOnWriteArrayList的全部内容,更多相关Java集合——CopyOnWriteArrayList内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复