通常情况下我们的高并发都发生在“多读少写”的情况,因此如果能够实现一种更优秀的算法这对生产环境还是很有好处的。ReadWriteLock当然是一种实现。CopyOnWriteArrayList/CopyOnWriteArraySet确实另外一种思路。
CopyOnWriteArrayList/CopyOnWriteArraySet
的基本思想是一旦对容器有修改,那么就“复制”一份新的集合,在新的集合上修改,然后将新集合复制给旧的引用。当然了这部分少不了要加锁。显然对于
CopyOnWriteArrayList/CopyOnWriteArraySet来说最大的好处就是“读”操作不需要锁了。
我们来看看源码。
/**
The array, accessed only via getArray/setArray.
*/
private
volatile
transient
Object[] array;
public
E get(
int
index) {
return
(E)(getArray()[index]);
}
private
static
int
indexOf(Object o, Object[] elements,
int
index,
int
fence) {
if
(o
==
null
) {
for
(
int
i
=
index; i
<
fence; i
++
)
if
(elements[i]
==
null
)
return
i;
}
else
{
for
(
int
i
=
index; i
<
fence; i
++
)
if
(o.equals(elements[i]))
return
i;
}
return
-
1
;
}
public
Iterator
<
E
>
iterator() {
return
new
COWIterator
<
E
>
(getArray(),
0
);
}
public
void
clear() {
final
ReentrantLock lock
=
this
.lock;
lock.lock();
try
{
setArray(
new
Object[
0
]);
}
finally
{
lock.unlock();
}
}
对于上述代码,有几点说明:
- List仍然是基于数组的实现,因为只有数组是最快的。
- 为了保证无锁的读操作能够看到写操作的变化,因此数组array是volatile类型的。
- get/indexOf/iterator等操作都是无锁的,同时也可以看到所操作的都是某一时刻array的镜像(这得益于数组是不可变化的)
- add/set/remove/clear等元素变化的都是需要加锁的,这里使用的是ReentrantLock。
这里有一段有意思的代码片段。
public
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();
}
}
final
void
setArray(Object[] a) {
array
=
a;
}
对于set操作,如果元素有变化,修改后setArray(newElements);将新数组赋值还好理解。那么如果一个元素没有变化,也就是上述代码的else部分,为什么还需要进行一个无谓的setArray操作?毕竟setArray操作没有改变任何数据。
对于这个问题也是很有意思,有一封邮件讨论了此问题(1
、2
、3
)。
大
致的意思是,尽管没有改变任何数据,但是为了保持“volatile”的语义,任何一个读操作都应该是一个写操作的结果,也就是读操作看到的数据一定是某
个写操作的结果(尽管写操作没有改变数据本身)。所以这里即使不设置也没有问题,仅仅是为了一个语义上的补充(个人理解)。
这里还有一个
有意思的讨论,说什么addIfAbsent在元素没有变化的时候为什么没有setArray操作?这个要看怎么理解addIfAbsent的语义了。如
果说addIfAbsent语义是”写“或者”不写“操作,而把”不写“操作当作一次”读“操作的话,那么”读“操作就不需要保持volatile语义
了。
对于CopyOnWriteArraySet而言就简单多了,只是持有一个CopyOnWriteArrayList,仅仅在add/addAll的时候检测元素是否存在,如果存在就不加入集合中。
private
final
CopyOnWriteArrayList
<
E
>
al;
/**
* Creates an empty set.
*/
public
CopyOnWriteArraySet() {
al
=
new
CopyOnWriteArrayList
<
E
>
();
}
public
boolean
add(E e) {
return
al.addIfAbsent(e);
}
分享到:
相关推荐
Java并发容器CopyOnWriteArrayList实现原理及源码分析 Java并发容器CopyOnWriteArrayList是Java并发包中提供的一个并发容器,实现了线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现。...
在Java的并发编程中,CopyOnWriteArrayList 是一个重要的线程安全集合类,它通过写时复制(Copy-On-Write)机制实现了高效的读操作。本文将详细探讨 CopyOnWriteArrayList 的工作原理、优缺点、适用场景以及代码示例...
Java中的`CopyOnWriteArrayList`是一个线程安全的列表实现,特别适合于高并发环境下的读多写少的场景。这个类的名字暗示了其工作原理:在修改(写入)时复制原有的数组,然后在新的数组上进行操作,最后将新数组替换...
第47节并发容器CopyOnWriteArrayList原理与使用00:15:52分钟 | 第48节并发容器ConcurrentLinkedQueue原理与使用00:31:03分钟 | 第49节Java中的阻塞队列原理与使用00:26:18分钟 | 第50节实战:简单实现消息队列00:...
并发容器CopyOnWriteArrayList原理与使用.mp4 并发容器ConcurrentLinkedQueue原理与使用.mp4 Java中的阻塞队列原理与使用.mp4 实战:简单实现消息队列.mp4 并发容器ConcurrentHashMap原理与使用.mp4 线程池的原理与...
Java集合类是Java编程中非常重要的组成部分,它们提供了一种组织和管理数据的方式。Java集合框架主要由两个...综上所述,Java集合类及其原理是面试中常见的知识点,理解并掌握它们有助于提升编程能力,解决实际问题。
2. **CopyOnWriteArrayList**: 一种特殊的列表实现,通过复制现有数组并在复制副本上进行修改来支持线程安全。 3. **ConcurrentLinkedQueue**: 提供了一个基于链表的无界队列实现,适用于高并发场景。 #### 五、...
第47节并发容器CopyOnWriteArrayList原理与使用00:15:52分钟 | 第48节并发容器ConcurrentLinkedQueue原理与使用00:31:03分钟 | 第49节Java中的阻塞队列原理与使用00:26:18分钟 | 第50节实战:简单实现消息队列00:...
### Java多线程与并发(14-26)-JUC集合-CopyOnWriteArrayList详解 #### 一、概述 `CopyOnWriteArrayList`是一种线程安全的集合类,它是`ArrayList`的一种特殊版本,主要通过复制底层数组的方式来保证线程安全性。...
CopyOnWriteArrayList的原理可以从“动态数组”和“线程安全”两个方面进一步进行说明: 1. CopyOnWriteArrayList的“动态数组”机制 -- 它内部有个“volatile数组”(array)来保持数据。在“添加/修改/删除”数据时...
5. **并发集合**:Java的并发集合类库,如ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue等,为并发环境下高效、安全的数据共享提供了支持。 6. **原子操作与CAS**:AtomicInteger、AtomicLong等...
这份宝典旨在帮助求职者深入理解Java的基础知识,掌握核心概念,以及对Java底层原理有深刻的认识。下面将详细讨论其中可能包含的重要知识点。 一、Java基础知识 1. 类与对象:讲解面向对象编程的基本概念,如封装、...
此外,面试者还应了解并发容器,如ConcurrentHashMap和CopyOnWriteArrayList的工作原理。 MyBatis作为常用的ORM框架,面试时会考察XML配置与注解方式的映射、动态SQL的使用、一级缓存与二级缓存的原理,以及如何...
3. 并发集合:探讨Java并发编程中的ConcurrentHashMap、CopyOnWriteArrayList等线程安全的集合类。 三、泛型 1. 泛型的概念:解释泛型的基本用法,如何限制类型参数,以及通配符的使用。 2. 泛型方法与泛型类:探讨...
6. **并发集合**:Java提供了线程安全的集合,如`ConcurrentHashMap`、`CopyOnWriteArrayList`等,这些集合在并发环境中性能优异。书中会分析它们的设计原理和使用场景。 7. **线程池**:`ExecutorService`是线程池...
对于Java集合框架,熟练掌握ArrayList、LinkedList、HashMap、HashSet、TreeMap等集合的特性和使用场景,以及并发集合如ConcurrentHashMap和CopyOnWriteArrayList的应用,能够体现候选人的实战经验。 除此之外,...
- **并发容器**:ConcurrentHashMap、CopyOnWriteArrayList等的线程安全特性。 通过深入学习这些知识点,并结合"Java面试题以及答案.pdf"中的题目进行实战练习,开发者可以更好地应对Java面试,提升自己的专业技能...
4. **并发集合**:Java并发包提供了线程安全的集合实现,如ConcurrentHashMap、CopyOnWriteArrayList和BlockingQueue等。这些集合在多线程环境中能保证数据一致性。 5. **死锁、活锁和饥饿**:文档可能会介绍这些...
4. **线程池**:Java的Executor框架是管理线程的重要工具,书中会详细介绍ThreadPoolExecutor的工作原理,以及如何配置和使用线程池来优化并发性能。 5. **并发设计模式**:书中可能会介绍一些常见的并发设计模式,...