目录
set(int index, E element)---修改元素
ArrayList我们都知道是List接口下的一个集合,那CopyOnWriteArrayList就是线程安全的ArrayList。它的使用和ArrayList完全相同,那它是怎么实现线程安全的呢?
底层代码基本的实现原理:
当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入, 而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器。
CopyOnWriteArrayList类是线程安全的,使用ReentrantLock加锁,底层数组采用Object类型的数组, volatile关键字可以保证变量的内存可见性。当一个变量被声明为volatile时,编译器和处理器会注意到这个变量可能会被其他线程并发地访问。这样可以避免线程之间的数据竞争,并确保多线程环境下变量的值是最新的。
// CopyOnWriteArrayList类实现了List<E>, RandomAccess, Cloneable, java.io.Serializable这几个接口
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 加锁(ReentrantLock可重入锁)
final transient ReentrantLock lock = new ReentrantLock();
// 设置为线程可见数组,用volatile修饰
private transient volatile Object[] array;
获取数组的方法,返回数组对象。
设置数组的方法,参数是一个数组a,把原数组的引用指向a
// 获取数组
final Object[] getArray() {
return array;
}
// 设置数组,将原数组的引用指向a
final void setArray(Object[] a) {
array = a;
}
无参构造方法
无参构造方法初始化一个长度为0的数组。
public CopyOnWriteArrayList() {
// 创建长度为0的数组
setArray(new Object[0]);
}
从array数组中根据下标读取值。
private E get(Object[] a, int index) {
return (E) a[index];
}
public E get(int index) {
return get(getArray(), index);
}
set(int index, E element)---修改元素
当修改数组的某个元素时,会将当前数组Copy复制新数组,在新数组中修改完后,最后替换原数组。整个过程加锁,保证线程的安全
// 修改指定下标的值
public E set(int index, E element) {
// 加锁,保证了在修改的过程中其他线程只允许做读的操作。
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 获取本地数组
Object[] elements = getArray();
// 获取本地数组index下标位置原来的值
E oldValue = get(elements, index);
// 判断原值与新值是否相同。
if (oldValue != element) {
// 如果原值与新值不同
// 先保存原来数组的长度
int len = elements.length;
// 拷贝一份原数组放入newElement中作为新数组
Object[] newElements = Arrays.copyOf(elements, len);
// 将新值赋给新数组对应的下标
newElements[index] = element;
// 将原数组的引用指向newElement
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
// 返回旧值
return oldValue;
} finally {
lock.unlock();
}
}
add()---添加元素
添加元素至集合末尾
添加元素至集合指定位置
给集合添加元素,先拷贝一个新集合,在新集合上添加,最后替换原数组
以上操作都是在加锁的情况下进行,保证了线程安全
// 添加值到数组末尾
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);
// 将要添加的值放入新数组的len下标的位置
newElements[len] = e;
// 将当前数组的引用指向新数组
setArray(newElements);
// 添加完成返回true
return true;
} finally {
lock.unlock();
}
}
// 给数组指定下标添加元素
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 判断下标大于或小于原数组长度,抛出下标越界异常
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
// 定义新数组
Object[] newElements;
// 获取要添加位置的后半部分元素个数
int numMoved = len - index;
// 如果后面元素个数为零
if (numMoved == 0)
// 同上在末尾添加
newElements = Arrays.copyOf(elements, len + 1);
else {
// 设置新数组长度为当前数组长度加1
newElements = new Object[len + 1];
// 拷贝前半部分
// 从原数组的下标0开始复制index个元素到新数组的下标0开始粘贴
System.arraycopy(elements, 0, newElements, 0, index);
// 拷贝后半部分
// 从原数组的下标index开始复制numMoved个元素到新数组的下标index+1开始粘贴
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
// 将要添加元素的值放入新数组下标index位置
newElements[index] = element;
// 将当前数组的引用指向新数组
setArray(newElements);
} finally {
lock.unlock();
}
}
remove---删除元素
1.删除指定位置上的元素
2.删除指定区间的元素
3.删除集合中元素,首先拷贝一个新数组,判断删除的元素是否在末尾若是末尾直接替换为原数组
若单个元素,数组长度加1;若为多个元素,数组长度减相应的长度。
若不是末尾元素则将分段拷贝成新数组
最终将替换为原数组
以上操作都是在加锁的情况下进行,保证了线程安全
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 获取原数组index下标位置的元素值
E oldValue = get(elements, index);
// 获取原数组后半部分元素个数减1
int numMoved = len - index - 1;
// 如果后半部分没有元素
if (numMoved == 0)
// 从原数组0下标开始拷贝len-1个元素,然后让当前数组引用指向新数组
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();
}
}
// 删除指定区间元素
void removeRange(int fromIndex, int toIndex) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 判断前后下标是否越界
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
// 获取删除后新数组的长度
int newlen = len - (toIndex - fromIndex);
// 计算toIndex下标后的元素个数
int numMoved = len - toIndex;
// 如果toIndex下标后没有元素
if (numMoved == 0)
// 拷贝原数组newlen前的元素,当前数组引用指向新数组
setArray(Arrays.copyOf(elements, newlen));
else {
Object[] newElements = new Object[newlen];
System.arraycopy(elements, 0, newElements, 0, fromIndex);
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
setArray(newElements);
}
} finally {
lock.unlock();
}
}
public boolean removeAll(Collection<?> c) {
// 如果集合为空,抛出空指针异常
if (c == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 判断原数组长度不为0
if (len != 0) {
// temp array holds those elements we know we want to keep
int newlen = 0;
// 创建一个长度为len的空数组
Object[] temp = new Object[len];
for (int i = 0; i < len; ++i) {
// 遍历获取原数组中的元素
Object element = elements[i];
// 如果遍历到的元素在要删除的集合中没有
if (!c.contains(element))
// 那就将这个元素存入刚创建的空集合中
temp[newlen++] = element;
}
// 如果删除完还有元素
if (newlen != len) {
setArray(Arrays.copyOf(temp, newlen));
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
clear()---清空
初始化为长度0的数组,该过程加锁,保证线程的安全
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
setArray(new Object[0]);
} finally {
lock.unlock();
}
}
addAll()--- 添加集合
替换为原数组(数组长度+添加集合长度)
以上操作都是在加锁的情况下进行,保证了线程安全
public boolean addAll(Collection<? extends E> c) {
// 判断当前集合的类型与CopyOnWriteArrayList的类型是否相同,
// 如果相同将c强制类型转换,将集合转换为数组,存入cs中
// 如果不相同直接toArray转换成数组,存入cs中
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
if (cs.length == 0)
return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 如果原数组的长度为0,并且当前数组的类型是Object类型
// 将cs写入原数组
if (len == 0 && cs.getClass() == Object[].class)
setArray(cs);
else {
// 如果原数组长度不为0
// 创建一个新数组,拷贝了原数组,长度是原数组长度加当前数组长度
Object[] newElements = Arrays.copyOf(elements, len + cs.length);
System.arraycopy(cs, 0, newElements, len, cs.length);
// 当前数组引用指向新数组
setArray(newElements);
}
return true;
} finally {
lock.unlock();
}
}
sort()---排序
es数组和newElements数组的引用指向相同的内存地址
public void sort(Comparator<? super E> c) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
// 创建新数组
Object[] newElements = Arrays.copyOf(elements, elements.length);
@SuppressWarnings("unchecked") E[] es = (E[])newElements;
// 直接用Arrays工具类中的sort方法,用comparator比较器进行排序
Arrays.sort(es, c);
setArray(newElements);
} finally {
lock.unlock();
}
}
equals()---比较
用私有方法eq进行比较
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
// 将o转换为List<>类型
List<?> list = (List<?>)(o);
// 创建迭代器
Iterator<?> it = list.iterator();
// 获取原数组
Object[] elements = getArray();
// 获取原数组长度
int len = elements.length;
// 遍历原数组
for (int i = 0; i < len; ++i)
// 如果it没有下一个元素或者eq方法返回值为false
if (!it.hasNext() || !eq(elements[i], it.next()))
return false;
// 如果it有下一个元素,返回false
if (it.hasNext())
return false;
return true;
}
private static boolean eq(Object o1, Object o2) {
//如果o1对象为null,则o2对象为null返回ture,不为null返回false;
//如果o1对象不为 null,则o1和o2内存地址相等返回true
return (o1 == null) ? o2 == null : o1.equals(o2);
}
hashCode()---计算哈希值
hashCode的值变为31*hashCode+元素不为空时的哈希值(调用Object类中的hashCode())
public int hashCode() {
// 初始化hashcode,值为1
int hashCode = 1;
// 获取原数组
Object[] elements = getArray();
// 获取原数组长度
int len = elements.length;
// 遍历原数组
for (int i = 0; i < len; ++i) {
// 将遍历到的元素的值赋值给obj对象
Object obj = elements[i];
// hashCode的值变为31*hashCode+元素不为空时的哈希值(调用Object类中的hashCode())
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
return hashCode;
}
}