面试题答案
一键面试CopyOnWriteArrayList读写机制
- 读操作:
- CopyOnWriteArrayList的读操作(如
get(int index)
)不会加锁。因为在读取时,直接访问内部数组,而不需要担心数组被修改,因为写操作是在新的数组上进行的。所以读操作是线程安全的,并且性能较高,因为不需要获取锁,避免了锁竞争带来的开销。
- CopyOnWriteArrayList的读操作(如
- 写操作:
- 写操作(如
add(E e)
)会加锁。当执行写操作时,先获取锁,然后复制当前数组到一个新数组,在新数组上进行修改操作,最后将内部数组引用指向新数组。例如,当执行add
方法时,先复制原数组,在新数组的末尾添加元素,然后将array
引用指向新的数组。这种方式保证了写操作的线程安全,但由于每次写操作都要复制数组,开销较大。
- 写操作(如
不同场景下性能表现
- 高并发读场景:
- 性能非常好。因为读操作无锁,多个线程可以同时进行读操作,不存在锁竞争,能充分利用多核CPU的优势,提升并发性能。
- 高并发写场景:
- 性能较差。每次写操作都要复制数组,这涉及内存分配和数据复制,开销巨大。而且由于加锁机制,在高并发写时,锁竞争会导致线程阻塞,大大降低性能。
- 读写混合场景:
- 如果读操作远多于写操作,性能相对较好。读操作不受写操作影响,仍能高效执行;但写操作时,读操作虽能继续,但写操作开销大,整体性能受写操作影响。如果写操作较多,性能会明显下降,因为写操作的高开销和锁竞争会影响整体效率。
与ConcurrentLinkedQueue比较
- 优势:
- 适合读多写少场景:CopyOnWriteArrayList在高并发读场景下性能优于ConcurrentLinkedQueue,因为读操作无锁,而ConcurrentLinkedQueue的读操作虽然也是无锁的,但由于链表结构的遍历开销,在大量读操作时性能不如CopyOnWriteArrayList。
- 数据一致性:CopyOnWriteArrayList能保证读操作的数据一致性,即读操作不会读取到部分修改的数据。因为读操作基于旧数组,写操作完成后才切换新数组。
- 劣势:
- 写操作性能:在高并发写场景下,ConcurrentLinkedQueue性能优于CopyOnWriteArrayList。ConcurrentLinkedQueue采用无锁算法(基于CAS操作),写操作不需要复制数据,而CopyOnWriteArrayList每次写都要复制数组,开销大。
- 内存开销:CopyOnWriteArrayList内存开销较大,因为写操作复制数组会导致短时间内有两份数组存在(旧数组和新数组)。而ConcurrentLinkedQueue基于链表结构,内存开销相对较小,且不需要像CopyOnWriteArrayList那样复制数据。