java中常用的线程安全集合类
java.util.Hashtable
HashTable的实例中有两个参数影响其性能:初始容量和加载因子
初始容量:Hash表创建时的容量
加载因子:对hash表在其容量自动增加之前可以达到多满的一个尺度,通常默认是0.75
Hashtable 的函数都是同步的,意味着它是线程安全的。它的key和value都不可以为null。此外,hashtable中的映射不是有序的。
线程安全原理–源码如下
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
private transient Entry<?,?>[] table;
public synchronized V put(K key, V value) {
....此处省略实现
}
public synchronized V remove(Object key) {
....此处省略实现
}
public synchronized void putAll(Map<? extends K, ? extends V> t) {
....此处省略实现
}
public synchronized V get(Object key) {
....此处省略实现
}
public synchronized void clear() {
....此处省略实现
}
}
从源码中可以看出,hashtable提供的几个主要方法,每个方法本身都是synchonizedde ,不会出现两个线程同时对数据进行操作的情况,因此是线程安全的
java.util.concurrent.ConcurrentHashMap
ConcurrentHashMap 继承AbstractMap 实现ConcurrentMap
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
}
java.util.concurrent.CopyOnWriteArrayList
CopyOnWriteArrayList 中的set、add、remove等方法,都使用了ReentrantLock的lock来加锁,unlock来解锁
当增加元素的时候使用Arrays.copyOf()来拷贝副本,在副本上增加元素,然后改变原来引用的指向副本。读操作不需要加锁,因此,CopyOnWriteArrayList类是一个线程安全的List接口实现,这对于读操作远远多于写操作的应用非常适合,特别是在并发的情况下,可以提供高性能的并发读取,并保证读取的内容一定是正确的,不受多线程并发问题的影响。
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
final transient ReentrantLock lock = new ReentrantLock();
private E get(Object[] a, int index) {
return (E) a[index];
}
}
java.util.concurrent.CopyOnWriteArraySet
原理同CopyOnWriteArrayList
CopyOnWrite机制介绍
CopyOnWrite容器即写是复制的容器。通俗的理解就是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素后,再将原容器的引用指向新的容器。这样做的好处就是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素
所以,CopyOnWrite容器也是一种读写分离的思想。读和写不容的容器。
我们从CopyOnWriteArrayList之前的分析和源码中可以看出,ArrayList里添加元素,在添加的时候是需要加锁的,否则多线程写的时候会copy出多个副本出来
读的时候不需要加锁,如果都的时候有多线程正在像ArrayList中添加数据,还是会读到旧的数据,因为写的时候不会锁住旧的ArrayList
1、使用场景:读多写少
2、使用注意点:
a、减少扩容开销;b、使用批量添加
缺点:
a、内存占用问题
b、数据一致性问题
Vector
与ArrayList不同,Vector中的操作是线程安全的,他是利用synchronized同步显示锁的机制实现的,实现机制类似Hashtable
StringBuffer和StringBuilder
StringBuffer:线程安全,效率不高,方法上使用synchronized实现线程安全机制
StringBuilder:线程不安全,效率相对高