IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java并发-CopyOnWriteArrayList -> 正文阅读

[Java知识库]Java并发-CopyOnWriteArrayList

简述

ArrayList不是一个线程安全的类,在我们开发中要让List线程安全可能会用到Vector,而Vector是直接在方法上用synchronized关键字实现线程同步的的,性能有比较大的问题,并发包中提供了CopyOnWriteArrayList,CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)上进行的,也就是使用了写时复制策略。性能会更好。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    //独占锁,后面发生对list的操作都要用到
    final transient ReentrantLock lock = new ReentrantLock();

    //存放具体的元素
    private transient volatile Object[] array;

    //...

}

lock和array是CopyOnWriteArrayList中两个比较重要的属性。

CopyOnWriteArrayList涉及修改的方法

CopyOnWriteArrayList中涉及到修改的方法都是要先拷贝一个数组,在拷贝数组上进行操作,之后再用拷贝的数组替换原本的数组。

// 用于修改数组中的元素
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 {
            //要更新的值如果和旧值是一样的,其实我们可以不用进行操作,这里还是重新设置了一下Array,是为了保证volatile语义
            setArray(elements);
        }
        return oldValue;
    } finally {
        // 释放锁
        lock.unlock();
    }
}
// 在数组中添加元素
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    // 拿到独占锁
    lock.lock();
    try {
        // 拿到存放元素的数组
        Object[] elements = getArray();
        int len = elements.length;
        // 拷贝出一个比元素组长度多1的新数组
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        // 新数组中插入元素
        newElements[len] = e;
        // 用新数组替换旧的数组
        setArray(newElements);
        return true;
    } finally {
        // 释放锁
        lock.unlock();
    }
}
// 数组中删除元素
public E remove(int index) {
    final ReentrantLock lock = this.lock;
    // 拿到独占锁
    lock.lock();
    try {
        // 拿到存放元素的数组
        Object[] elements = getArray();
        int len = elements.length;
        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();
    }
}

每一个涉及到变动数组的操作都要先获取锁,这样是为了保证同一个时刻只有一个线程在修改数组。而每一次更新的错都是操作拷贝的数组,这样原本在就数组上进行的查询、迭代就可以不受影响,这就是写时复制。

获得元素

// 获取元素的入口
public E get(int index) {
    return get(getArray(), index);
}

// 获得集体元素
private E get(Object[] a, int index) {
    return (E) a[index];
}

//获得存放元素的数组
final Object[] getArray() {
    return array;
}

通过代码可以看到获取元素时分成两个步骤的:

  1. 获取数组
  2. 通过下标获取具体的元素

如果没有进行处理,那么这样的步骤在并发下就有可能出现问题,但是由于发生变动的操作都是同步的,并且时写时复制的,所以就算在第一步第二步直接发生了数据变化,查询也不会被影响到。(因为操作的是修改之前的数组,而变更操作更新的都是复制之后的新数组)

总结

CopyOnWriteArrayList使用写时复制的策略来保证list的一致性,而获取―修改—写入三步操作并不是原子性的,所以在增删改的过程中都使用了独占锁,来保证在某个时间只有一个线程能对list数组进行修改。另外在查询期间,其他线程对list的修改是不可见的,查询的数组是一个快照。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-20 14:57:12  更:2021-08-20 14:59:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 0:08:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码