本文涉及到的集合均继承于Collection接口,此篇不讨论继承于Map的集合对象
既然本文讲的是线程安全的集合,那么开门见山。Java中目前能基本保证线程安全集合的操作方式有如下三种:Vector对象、Collections.SynchronizedList对象、CopyOnWriteArrayList集合对象
Vector对象:线程安全机制为使用synchronized关键字对方法进行加锁,属于爷爷辈的线程安全对象了。从下面的源码可以知道底层为数组,初始大小为10。扩容机制为达到最大容量之后进行原数组容量*2的扩容,扩容因子为1,最大容量为Integer.MAX_VALUE。万幸的是Vector类的iterator方法为线程安全方法
/**
* The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
*
* <p>Any array elements following the last element in the Vector are null.
*
* @serial
*/
protected Object[] elementData;
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
public Vector() {
this(10);
}
//下面为扩容源码的执行链
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
/**
* This helper method split out from add(E) to keep method
* bytecode size under 35 (the -XX:MaxInlineSize default value),
* which helps when add(E) is called in a C1-compiled loop.
*/
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
elementCount = s + 1;
}
private Object[] grow() {
return grow(elementCount + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
/**
* Returns a capacity at least as large as the given minimum capacity.
* Will not return a capacity greater than MAX_ARRAY_SIZE unless
* the given minimum capacity is greater than MAX_ARRAY_SIZE.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity <= 0) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
从下面的继承图可以发现Vector对象继承了Cloneable、Serializable、RandomAccess接口,代表Vector支持拷贝、序列化、随机访问功能
Collections.SynchronizedList对象:个人认为它更像一个线程安全的容器,从这个对象的构造方法可以感知一二
而 SynchronizedList能够保证线程安全也是通过父类SynchronizedCollection,父类构造方法如下。mutex就是在这个容器中保证线程安全的锁对象。大致逻辑就是通过SynchronizedCollection中设置mutex对象,对mutex加锁后再调用List<E> list的相关方法来保证线程安全。
但是有一点需要注意,同样也在源码中列举出来了,就是咱们的iterator方法是没有对mutex加锁的。所以在调用迭代器进行for循环读取前记得用synchronized关键将参数list锁住。
//SynchronizedCollection构造逻辑
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
}
//SynchronizedCollection构造逻辑结束
//举两个SynchronizedCollection源码例子
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public boolean remove(Object o) {
synchronized (mutex) {return c.remove(o);}
}
//SynchronizedList的迭代器方法
public ListIterator<E> listIterator() {
return list.listIterator(); // Must be manually synched by user
}
public ListIterator<E> listIterator(int index) {
return list.listIterator(index); // Must be manually synched by user
}
CopyOnWriteArrayList对象:
比较一下和ArrayList继承树的区别,说实话没啥区别
那 CopyOnWriteArrayList对象怎么去进行线程同步就需要从源码找答案,如下:
可以发现 CopyOnWriteArrayList构建了一个lock对象和array对象数组,同步也是从这一块开始的。首先CopyOnWriteArrayList的方法通过synchronized关键字锁住lock对象,对调用对象进行上锁(对象锁),保证多线程的原子性。同时对象数组array被关键字volatile修饰,代表在读取array的时候直接读取内存中的值,保证多线程的可见性。通过上述两方面进行线程安全的控制,顺带多一句嘴,这个类适合查询多于修改、删除的业务场景。原因在于:在改删场景使用的方法是通过clone来新建对象数组赋值给array,这样对于内存是一种损耗。
/**
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this list. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all the elements in this list
*/
public Object[] toArray() {
return getArray().clone();
}
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
synchronized (lock) {
Object[] es = getArray();
E oldValue = elementAt(es, index);
if (oldValue != element) {
es = es.clone();
es[index] = element;
}
// Ensure volatile write semantics even when oldvalue == element
setArray(es);
return oldValue;
}
}