1、List
1.1 ArrayList不是线程安全的。多线程情况下操作ArrayList 不是线性安全的,可能会抛出java.util.ConcurrentModificationException
并发修改异常。
1.2 Vector是线程安全的。Vector对add和get方法都加了关键字synchronized修饰,所以是线程安全的,但是同一时间只能有一个线程访问进行读写,所以性能慢。
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
1.3 Collections.synchronizedList(List<T> list) 通过Collections的synchronizedList静态方法,可以返归一个线程安全的SynchronizedList对象。SynchronizedList是Collections的静态内部类。
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
synchronizedLis 和 Vector的区别
- 1 synchronizedList的实现中,synchronized关键字修饰代码块;Vector的实现中修饰方法。
- 2 synchronizedList只封装了add、get、remove等代码块,但Iterator却不是同步的,进行遍历时要手动进行同步处理;Vector中对Iterator也进行了加锁。
- 3 synchronizedList能够将所有List实现类封装为同步集合,其内部持有的仍然是List的实现类(ArrayList/LinkedList),所以除同步外,几乎只有该实现类和Vector的区别。
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
1.4 CopyOnWriteArrayList<E> 写入时复制(CopyOnWrite)思想
数据存放在一个Object数组中,在进行add和set操作时会上锁,同时copy一份原始的Object数组,在完成add和set操作后,重新setArray,释放锁。get操作没有加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,不会阻塞,读还是可以读到旧的数据。
private transient volatile Object[] array;
public E get(int index) {
return get(getArray(), index);
}
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 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();
}
}
一个测试各种List是否安全的例子:
public static void main(String[] args) {
//List<String> list = new ArrayList<>();
//List<String> list = new Vector<>();
//List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 7));
System.out.println(list);
}).start();
}
}
2、Map
2.1 HashMap<>不是线程安全的。多线程情况下操作HashMap不是线性安全的,可能会抛出java.util.ConcurrentModificationException
并发修改异常。
HashMap就有点复杂了,1.7由数组+链表实现,1.8由数组+链表+红黑树实现,不深入了,得单独学。
2.2 Collections.synchronizedMap( new HashMap<>()) 通过Collections的synchronizedMap静态方法,可以返归一个线程安全的SynchronizedMap对象。SynchronizedMap是Collections的静态内部类。get和put都加锁
public V get(Object key) {
synchronized (mutex) {return m.get(key);}
}
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
2.3 class ConcurrentHashMap<K,V> 来自 java.util.concurrent.ConcurrentHashMap,支持检索的完全并发性和更新的高预期并发性的哈希表,单独学。
一个测试各种Map是否安全的例子:
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
//Map<String,String> map= Collections.synchronizedMap( new HashMap<>());
//Map<String,String> map=new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
map.put(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}).start();
}
}
3、Set
3.1 class HashSet<E>不是线程安全的。
HashSetd底层由HashMap实现,HashSet的add方法调用对的是HashMap的put方法,传输的key是将传入set的元素,传入的value是一个固定的Object对象。
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public Iterator<E> iterator() {
return map.keySet().iterator();
}
3.2 class TreeSet<E> 不是线程安全的。
3.3 Collections.synchronizedSet(new HashSet<>()) 是线程安全的。
3.4 class CopyOnWriteArraySet<E> 是线程安全的。
一个测试各种Set是否安全的例子:
public static void main(String[] args) {
Set<String> set = new HashSet<>();
//Set<String > set =new TreeSet<>();
//Set<String> set=Collections.synchronizedSet(new HashSet<>());
//Set<String> set=new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 7));
System.out.println(set);
}).start();
}
}