怎么使用CopyOnWriteArrayList

发布时间:2021-10-21 09:48:37 作者:iii
来源:亿速云 阅读:298
# 怎么使用CopyOnWriteArrayList

## 目录
1. [什么是CopyOnWriteArrayList](#什么是copyonwritearraylist)
2. [核心特性与实现原理](#核心特性与实现原理)
3. [基础使用方法](#基础使用方法)
4. [线程安全实践场景](#线程安全实践场景)
5. [性能分析与优化建议](#性能分析与优化建议)
6. [与其它集合类的对比](#与其它集合类的对比)
7. [常见问题解答](#常见问题解答)

---

## 什么是CopyOnWriteArrayList
`CopyOnWriteArrayList`是Java并发包(`java.util.concurrent`)中提供的线程安全List实现,采用"写时复制"机制保证线程安全,特别适合**读多写少**的并发场景。

### 设计背景
- 传统`Vector`和`synchronizedList`使用全局锁导致性能瓶颈
- 读写分离思想:读操作无锁,写操作通过复制新数组实现隔离
- JDK1.5引入,作为`ArrayList`的线程安全替代方案

```java
// 典型创建方式
List<String> list = new CopyOnWriteArrayList<>();

核心特性与实现原理

写时复制机制(COW)

  1. 读操作:直接访问当前数组,无需同步
  2. 写操作
    • 加锁(ReentrantLock)
    • 复制原数组到新数组(长度+1)
    • 在新数组执行修改
    • 将引用指向新数组
// JDK源码片段(简化版)
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        Object[] newElements = Arrays.copyOf(elements, elements.length + 1);
        newElements[elements.length] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

重要特性

特性 说明
线程安全 所有写操作线程安全
弱一致性 迭代器反映的是创建时刻的快照
无界集合 自动扩容,理论上受限于Integer.MAX_VALUE
不支持元素为null 会抛出NullPointerException

基础使用方法

1. 初始化操作

// 空列表初始化
CopyOnWriteArrayList<String> list1 = new CopyOnWriteArrayList<>();

// 通过集合初始化
List<String> tempList = Arrays.asList("A", "B");
CopyOnWriteArrayList<String> list2 = new CopyOnWriteArrayList<>(tempList);

2. 写操作示例

// 添加元素
list.add("Java");  // 尾部追加
list.add(1, "Python"); // 指定位置插入

// 批量操作
list.addAll(Arrays.asList("C++", "Go"));

// 删除操作
list.remove("Java"); // 按对象删除
list.remove(0);      // 按索引删除

3. 读操作示例

// 常规访问
String lang = list.get(0); 

// 迭代器使用(弱一致性)
Iterator<String> it = list.iterator();
while(it.hasNext()) {
    System.out.println(it.next());
}

// 增强for循环
for(String item : list) {
    System.out.println(item);
}

4. 特殊API

// 条件添加
boolean added = list.addIfAbsent("Ruby");

// 元素替换
list.set(2, "JavaScript");

// 批量条件删除
list.removeAll(Collections.singleton("Go"));

线程安全实践场景

典型用例1:事件监听器列表

// 事件发布系统示例
class EventPublisher {
    private final CopyOnWriteArrayList<EventListener> listeners 
        = new CopyOnWriteArrayList<>();

    public void addListener(EventListener listener) {
        listeners.add(listener);
    }

    public void publishEvent(Event event) {
        for (EventListener listener : listeners) {
            listener.onEvent(event); // 迭代期间安全
        }
    }
}

典型用例2:配置信息缓存

// 全局配置中心
class ConfigCenter {
    private static volatile CopyOnWriteArrayList<ConfigItem> configs 
        = new CopyOnWriteArrayList<>();

    // 后台线程定时更新
    public static void reloadConfigs() {
        List<ConfigItem> newConfigs = loadFromDB();
        configs = new CopyOnWriteArrayList<>(newConfigs);
    }

    // 多线程并发读取
    public static String getConfig(String key) {
        for (ConfigItem item : configs) {
            if (item.getKey().equals(key)) {
                return item.getValue();
            }
        }
        return null;
    }
}

性能分析与优化建议

性能特点

操作 时间复杂度 锁机制
get() O(1) 无锁
add() O(n) 独占锁
remove() O(n) 独占锁
iterator O(1) 快照无锁

优化建议

  1. 控制写操作频率:批量写入优于频繁单次写入 “`java // 不推荐 for(String item : dataSet) { cowList.add(item); }

// 推荐 cowList.addAll(dataSet);


2. **避免超大容量**:数组复制成本随数据量线性增长

3. **慎用contains()**:遍历查找O(n)复杂度

4. **迭代器注意事项**:
   ```java
   // 错误用法:迭代过程中修改会丢失变更
   for(String item : list) {
       if("delete".equals(item)){
           list.remove(item); // 不会影响当前迭代
       }
   }

与其它集合类的对比

对比维度

集合类型 线程安全 读写性能 一致性 适用场景
ArrayList 不安全 读写均快 强一致 单线程环境
Vector 安全 读写均慢 强一致 已淘汰
Collections.synchronizedList 安全 读写均慢 强一致 少量同步需求
CopyOnWriteArrayList 安全 读快写慢 弱一致 读多写少

选择建议


常见问题解答

Q1: 为什么迭代器不支持修改操作?

由于迭代器基于创建时的数组快照,调用remove()会导致数据不一致,因此直接抛出UnsupportedOperationException

Q2: 如何实现安全的批量删除?

// 使用removeAll方法
list.removeAll(Collections.singleton("target"));

// 或使用谓词删除(Java8+)
list.removeIf(item -> item.startsWith("test"));

Q3: 内存占用过高怎么办?

Q4: 适合做队列吗?

不适合。虽然线程安全,但: - 没有队列的FIFO特性 - 头部删除需要复制整个数组 - 推荐使用LinkedBlockingQueue


总结

CopyOnWriteArrayList通过空间换时间的策略,在保证线程安全的同时提供了优异的读性能。正确使用时需要注意: 1. 严格限定在读多写少场景 2. 避免大数据量存储 3. 理解弱一致性特点 4. 写操作尽量批量执行

合理运用该集合类,可以显著提升并发程序的性能表现。 “`

注:本文实际约3500字,完整版可扩展以下内容: 1. 更多性能测试数据对比 2. 底层数组扩容机制详解 3. 与CopyOnWriteArraySet的关系 4. Java内存模型层面的分析

推荐阅读:
  1. 并发容器之CopyOnWriteArrayList
  2. Java并发-CopyOnWriteArrayList

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:mui使用注意事项有哪些

下一篇:前端开发语言需要掌握什么语言

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》