线程安全集合类的分类
【遗留的线程安全集合】
增删改查方法大多直接使用 synchronized 修饰,性能较低,不推荐使用。
- HashTable
- Vector
【使用 Collections 修饰的线程安全集合】
使用装饰器模式,把原集合类的方法使用 synchronized 修饰一遍,性能较低,不推荐使用。
- Collections.synchronizedCollection
- Collections.synchronizedList
- Collections.synchronizedMap
- Collections.synchronizedSet
- Collections.synchronizedNavigableMap
- Collections.synchronizedNavigableSet
- Collections.synchronizedSortedMap
- Collections.synchronizedSortedSet
【java.util.concurrent 包下的线程安全集合】
里面包含三类关键词:
- Blocking 大部分实现基于锁,并提供用来阻塞的方法。
- CopyOnWrite之类容器修改开销相对较重。
- Concurrent 类型的容器,内部很多操作使用cas优化,一般可以提供较高吞吐量。
- 遍历弱一致性问题。当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍历,这时内容是旧的。
- 大小弱一致性问题。获取 size 操作未必是100%准确。
- 读取弱一致性问题。
对于非安全容器来讲,遍历时如果发生了修改,则使用 fail-fast 机制让遍历立刻失败,抛出
ConcurrentModificationException,不再继续遍历。
ConcurrentHashMap
computeIfAbsent 方法
使用普通 HashMap,线程不安全。
HashMap<Character, Integer> map = new HashMap<>();for (int i = 0; i < 100; i++) {new Thread(() -> {Integer counter = map.get(key);int count = counter == null ? 1 : counter + 1;map.put(key, count);}).start();}
使用普通 HashMap + synchronized,线程安全,但是效率降低。
HashMap<Character, Integer> map = new HashMap<>();for (int i = 0; i < 100; i++) {new Thread(() -> {synchronized (map){Integer counter = map.get(key);int count = counter == null ? 1 : counter + 1;map.put(key, count);}}).start();}
使用 ConcurrentHashMap,线程不安全。
ConcurrentHashMap<Character, Integer> map = new ConcurrentHashMap<>();for (int i = 0; i < 100; i++) {new Thread(() -> {// get()和put()组合在一起不是原子操作Integer counter = map.get(key);int count = counter == null ? 1 : counter + 1;map.put(key, count);}).start();}
使用 ConcurrentHashMap + AtomicInteger,线程安全,且效率相对较高。。
ConcurrentHashMap<Character, AtomicInteger> map = new ConcurrentHashMap<>();for (int i = 0; i < 100; i++) {new Thread(() -> {// 如果key不存在,则调用回调方法生成一个value,存入并返回// 如果key存在,则直接返回valueAtomicInteger integer = map.computeIfAbsent(key, (key) -> new AtomicInteger());// 执行累加integer.getAndIncrement();}).start();}
