集合与高并发
List
ArrayList,线程不安全
源码中的add方法不是线程安全的,size++不是原子性操作
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Collections.synchronizedList(new ArrayList<>()),适用读写平衡
根据传入的List是否实现随机访问接口返回不同的实现
内部维护了一个final Object作为锁对象,读写都加了synchronized
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
......
}
读操作增加了synchronized,性能比CopyOnWriteArrayList差
写操作增加了synchronized,性能比CopyOnWriteArrayList好
遍历操作没加锁,所以我们要锁
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
CopyOnWriteArrayList,适用读多写少
实现原理:
在列表有更新时直接将原有的列表复制一份,并再新的列表上进行更新操作,完成后再将引用移到新的列表上。旧列表如果仍在使用中(比如遍历)则继续有效。如此一来就不会出现修改了正在使用的对象的情况(读和写分别发生在两个对象上),同时读操作也不必等待写操作的完成,免去了锁的使用加快了读取速度
写操作:
/** The lock protecting all mutators */
transient final ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array;//保证了线程的可见性
public boolean add(E e) {
final ReentrantLock lock = this.lock;//ReentrantLock 保证了线程的可见性和顺序性,即保证了多线程安全。
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);//在原先数组基础之上新建长度+1的数组,并将原先数组当中的内容拷贝到新数组当中。
newElements[len] = e;//设值
setArray(newElements);//对新数组进行赋值
return true;
} finally {
lock.unlock();
}
}
读操作:
public E get(int index) {
return (E)(getArray()[index]);
}
Vector,线程安全,已淘汰
Map
HashMap,线程安全
Fast-Fail(遍历时写入操作异常),modCount记录了HashMap被修改的次数,遍历会进行检查,发现值被修改了就抛异常
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null);
}
}
hash方法,做位运算,返回对象下标位置
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
ConcurrentHashMap,线程安全
JDK1.7使用分段锁,JDK1.8使用Synchronized 同步锁,通过 Synchronized 实现 HashEntry 作为锁粒度,弱一致性
读操作
Unsafe#getObjectVolatile
写操作
在没有哈希冲突的情况下,会使用 Unsafe#compareAndSetObject进行添加元素操作;如果有冲突,则通过 Synchronized 将链表锁定,再执行接下来的操作
HashTable,线程安全,已淘汰
所有方法都使用了synchronized
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable,
java.io.Serializable {
public synchronized V put(K key, V value) {
// 省略代码
}
public synchronized V remove(Object key) {
}
public synchronized V get(Object key) {
}
public synchronized int size() {
return count;
}
}