JDK容器与并发—List—CopyOnWriteArrayList

概述

      俗称读写ArrayList,线程安全的ArrayList版本,其所有写操作都是基于底层数组的副本操作,成功后再替换底层数组,可以理解为基于"snapshot" array的并发,适用于读多写少的并发环境。

1)采用"snapshot"  iterator,在遍历过程中,底层数组是不会修改的,不存在并发干扰,不会抛出ConcurrentModificationException异常;

2)遵守内存一致性:一个线程中先于“某元素添加到CopyOnWriteArrayList”的操作happen-before另一个线程中后于“从中获取或删除该元素”的操作。

数据结构

      基于数组,采用一把写锁:

    /** The lock protecting all mutators */
    transient final ReentrantLock lock = new ReentrantLock();

    /** The array, accessed only via getArray/setArray. */
    private volatile transient Object[] array;

构造器

// 无参构造,将底层数组设置为空数组
public CopyOnWriteArrayList() {
	setArray(new Object[0]);
}

// 带Collection参数构造
public CopyOnWriteArrayList(Collection<? extends E> c) {
	Object[] elements = c.toArray();
	// c.toArray might (incorrectly) not return Object[] (see 6260652)
	if (elements.getClass() != Object[].class)
		elements = Arrays.copyOf(elements, elements.length, Object[].class);
	setArray(elements);
}

// 带数组参数构造
public CopyOnWriteArrayList(E[] toCopyIn) {
	setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

增删改查

public boolean add(E e) {
	final ReentrantLock lock = this.lock;
	lock.lock(); // 获取写锁
	try {
		Object[] elements = getArray();
		int len = elements.length;
		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 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 {
			// Not quite a no-op; ensures volatile write semantics
			setArray(elements);
		}
		return oldValue;
	} finally {
		lock.unlock();// 释放写锁
	}
}

public E get(int index) {
	return get(getArray(), index);
}

迭代器

      采用基于底层数组的快照,不支持写操作:remove、set、add,一个ListIterator:

private static class COWIterator<E> implements ListIterator<E> {
	/** Snapshot of the array */
	private final Object[] snapshot;
	/** Index of element to be returned by subsequent call to next.  */
	private int cursor;

	private COWIterator(Object[] elements, int initialCursor) {
		cursor = initialCursor;
		snapshot = elements;
	}

	public boolean hasNext() {
		return cursor < snapshot.length;
	}

	public boolean hasPrevious() {
		return cursor > 0;
	}

	@SuppressWarnings("unchecked")
	public E next() {
		if (! hasNext())
			throw new NoSuchElementException();
		return (E) snapshot[cursor++];
	}

	@SuppressWarnings("unchecked")
	public E previous() {
		if (! hasPrevious())
			throw new NoSuchElementException();
		return (E) snapshot[--cursor];
	}

	public int nextIndex() {
		return cursor;
	}

	public int previousIndex() {
		return cursor-1;
	}

	public void remove() {
		throw new UnsupportedOperationException();
	}

	public void set(E e) {
		throw new UnsupportedOperationException();
	}

	public void add(E e) {
		throw new UnsupportedOperationException();
	}
}

特性

      如何实现线程安全?

/** The lock protecting all mutators */
transient final ReentrantLock lock = new ReentrantLock();

/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array;

// array volatile读
final Object[] getArray() {
	return array;
}

// array volatile写
final void setArray(Object[] a) {
	array = a;
}

CopyOnWriteArrayList的线程安全体现在读与写、写与写的线程安全:

1)所有写操作add、remove、set等共用lock一把锁;

2)所有写操作如果有修改,则对array volatile写;

3)所有读操作首先进行的是array volatile读;

疑问

1)为什么中的无用setArray(elements);不能去掉?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值