CopyOnWriteArrayList的addIfAbsent()方法

 大致执行过程:首先获取当前数组存取元素,然后找我们要插入数据的下标值,找到了直接返回false,没找到的话,上锁,再次判断数组是否发生变化,这里多一次判断,我认为是为了防止这种可能性的出现:在判断完是否存在元素和加锁之间,另一个线程加入了我们要加的元素,不判断的话,那么同一个元素就有可能出现俩次,违背了addIfAbsent的意愿。加锁之后,如果数组并没有变的话,那么就执行CopyOnWriteArrayList的老套路,将原来的数据放到一个比原来长度长1的数组,最后一个空位放我们要放的数据。通过上述过程就实现了数据的唯一性,CopyOnWriteSet底层实现就是利用CopyOnWriteArrayList,唯一性就是利用addifabsent方法。

public boolean addIfAbsent(E e) {
    // 获取元素数组, 取名为快照
    Object[] snapshot = getArray();
    // 检查如果元素存在,直接返回false
    // 如果不存在再调用addIfAbsent()方法添加元素
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
        addIfAbsent(e, snapshot);
}

private boolean addIfAbsent(E e, Object[] snapshot) {
    final ReentrantLock lock = this.lock;
    // 加锁
    lock.lock();
    try {
        // 重新获取旧数组
        Object[] current = getArray();
        int len = current.length;
        // 如果快照与刚获取的数组不一致
        // 说明有修改
        if (snapshot != current) {
            // 重新检查元素是否在刚获取的数组里
            int common = Math.min(snapshot.length, len);
            for (int i = 0; i < common; i++)
                // 到这个方法里面了, 说明元素不在快照里面
                if (current[i] != snapshot[i] && eq(e, current[i]))
                    return false;
            if (indexOf(e, current, common, len) >= 0)
                    return false;
        }
        // 拷贝一份n+1的数组
        Object[] newElements = Arrays.copyOf(current, len + 1);
        // 将元素放在最后一位
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        // 释放锁
        lock.unlock();
    }
}
### CopyOnWriteArrayList 的 `simulate` 方法实现与用法 #### 什么是 CopyOnWriteArrayListCopyOnWriteArrayListJava 中的一个线程安全的集合类,属于 `java.util.concurrent` 包的一部分。它的核心特性在于写操作时会创建底层数组的新副本[^1]。这种机制使得读操作可以无锁地进行,从而提高了并发性能。 尽管标准库中并没有提供名为 `simulate` 的方法,可以通过自定义方式来模拟其行为或者展示如何使用该类的一些典型功能。以下是关于其实现和使用的详细介绍: --- #### 自定义 `simulate` 方法示例 假设我们需要通过一个 `simulate` 方法演示 CopyOnWriteArrayList 的基本特性和优势,则可以按照如下方式进行设计: ```java import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class CopyOnWriteSimulator { public static void simulate(List<String> list) { System.out.println("Initial List: " + list); // 添加新元素并观察底层数组的变化 addElements(list); System.out.println("After Adding Elements: " + list); // 删除某些元素并验证一致性 removeElements(list); System.out.println("After Removing Elements: " + list); // 并发访问测试 concurrentAccessTest(list); } private static void addElements(List<String> list) { for (int i = 0; i < 5; i++) { String element = "Element-" + i; list.add(element); // 每次修改都会复制整个数组 } } private static void removeElements(List<String> list) { int size = Math.min(3, list.size()); // 防止越界 for (int i = 0; i < size; i++) { list.remove(i); // 移除指定索引位置的元素 } } private static void concurrentAccessTest(List<String> list) { Thread t1 = new Thread(() -> { for (String s : list) { System.out.println(Thread.currentThread().getName() + ": Reading " + s); } }); Thread t2 = new Thread(() -> { try { Thread.sleep(100); // 让另一个线程先运行一段时间 } catch (InterruptedException e) { e.printStackTrace(); } list.add("Concurrent Element"); }); t1.start(); t2.start(); try { t1.join(); // 等待线程结束 t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final List After Concurrent Access: " + list); } public static void main(String[] args) { List<String> cowList = new CopyOnWriteArrayList<>(); simulate(cowList); } } ``` 上述代码展示了以下几个方面: - **初始状态打印**:显示列表初始化后的内容。 - **添加元素**:通过循环向列表中追加多个字符串项。 - **删除元素**:移除部分已存在的条目。 - **并发访问测试**:启动两个独立线程分别负责读取和修改数据结构的内容。 --- #### 工作原理分析 CopyOnWriteArrayList 的主要特点是基于不可变性的设计理念。每当发生任何结构性变更(如增加或删除),它并不会直接修改现有的内部数组,而是克隆一份新的实例作为替代。因此,在多线程环境下能够有效避免竞争条件的发生。 具体来说: - 所有迭代器都只作用于原始版本的数据集上,即使其他地方正在进行更新也不会受到影响。 - 对于频繁发生的查询请求而言效率较高;但对于大规模批量插入/删除动作则开销较大,因为每次都需要重新分配内存空间以及拷贝全部现有记录。 此外需要注意的是,由于每一次改动都要生成完整的备份,所以在高频率写入场景下可能会带来显著的资源消耗问题[^4]。 --- #### 应用场景举例 考虑到以上优缺点权衡之后,通常推荐将此容器应用于那些读远超写的场合之中。比如日志记录系统中的事件缓冲区管理就是一个很好的案例——外部模块不断提交新的追踪信息进来,而内部处理单元周期性抽取出来做进一步加工整理即可[^3]。 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值