线程安全集合类的分类

【遗留的线程安全集合】

增删改查方法大多直接使用 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,线程不安全

  1. HashMap<Character, Integer> map = new HashMap<>();
  2. for (int i = 0; i < 100; i++) {
  3. new Thread(() -> {
  4. Integer counter = map.get(key);
  5. int count = counter == null ? 1 : counter + 1;
  6. map.put(key, count);
  7. }).start();
  8. }

使用普通 HashMap + synchronized,线程安全,但是效率降低。

  1. HashMap<Character, Integer> map = new HashMap<>();
  2. for (int i = 0; i < 100; i++) {
  3. new Thread(() -> {
  4. synchronized (map){
  5. Integer counter = map.get(key);
  6. int count = counter == null ? 1 : counter + 1;
  7. map.put(key, count);
  8. }
  9. }).start();
  10. }

使用 ConcurrentHashMap,线程不安全

  1. ConcurrentHashMap<Character, Integer> map = new ConcurrentHashMap<>();
  2. for (int i = 0; i < 100; i++) {
  3. new Thread(() -> {
  4. // get()和put()组合在一起不是原子操作
  5. Integer counter = map.get(key);
  6. int count = counter == null ? 1 : counter + 1;
  7. map.put(key, count);
  8. }).start();
  9. }

使用 ConcurrentHashMap + AtomicInteger,线程安全,且效率相对较高。。

  1. ConcurrentHashMap<Character, AtomicInteger> map = new ConcurrentHashMap<>();
  2. for (int i = 0; i < 100; i++) {
  3. new Thread(() -> {
  4. // 如果key不存在,则调用回调方法生成一个value,存入并返回
  5. // 如果key存在,则直接返回value
  6. AtomicInteger integer = map.computeIfAbsent(key, (key) -> new AtomicInteger());
  7. // 执行累加
  8. integer.getAndIncrement();
  9. }).start();
  10. }