1、Collections包装的同步容器
在Collections类中,通过以下方法可以将一个非线程安全的容器包装为一个线程安全的容器:
//线程安全的List,其类型为SynchronizedList或SynchronizedRandomAccessList
List<Object> list = Collections.synchronizedList(new ArrayList<>());
//线程安全的Set,其类型为SynchronizedSet
Set<Object> set = Collections.synchronizedSet(new HashSet<>());
//线程安全的Map,其类型为synchronizedMap
Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());
除此之外还有SynchronizedCollection、SynchronizedSortedSet、SynchronizedSortedMap、SynchronizedNavigableSet和SynchronizedNavigableMap都是线程安全的。
它们的实现原理都是直接将操作容器的方法通过synchronized同步关键字包裹起来,以SynchronizedList为例:
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return list.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return list.hashCode();}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
public int indexOf(Object o) {
synchronized (mutex) {return list.indexOf(o);}
}
public int lastIndexOf(Object o) {
synchronized (mutex) {return list.lastIndexOf(o);}
}
public boolean addAll(int index, Collection<? extends E> c) {
synchronized (mutex) {return list.addAll(index, c);}
}
......
2、JDK早期实现的同步容器
Vector、Stack 和 Hashtable是JDK1.5之前提供的同步容器,其实现原理与SynchronizedCollection基本类似,都是通过synchronized 修饰操作容器的方法实现的,只不过它们操作容器的方法都是自己实现的,而不是简单地包装其他非线程安全的容器的方法。以下为Vector中的一段核心源码:
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
public synchronized void removeAllElements() {
modCount++;
// Let gc do its work
for (int i = 0; i < elementCount; i++)
elementData[i] = null;
elementCount = 0;
}
juc包中的并发容器
JDK1.5之前的线程安全的容器中的方法都是通过synchronized 来保证的,这样有个最大的问题,那就是性能差。因为它们在方法实现上都是使用synchronized 锁住整个容器,因此访问容器的方法串行度非常高。
在JDK1.5及之后的版本中,在java.util.concurrent并发包中提供了很多的并发容器,包含了我们常用的四大类容器:List、Map、Set 和 Queue。