`

【Java核心-进阶】并发工具包——线程安全的 List、Map、Set

    博客分类:
  • Java
 
阅读更多

三大并发类:Concurrent、CopyOnWrite、Blocking

Java并发包中的集合从线程安全实现方式而言可分为三类:Concurrent、CopyOnWrite、Blocking。

  • Concurrent 类的集合基于 lock-free 的方式实现。严格来说,它们是真正的并发。适合实现较高的吞吐量。
  • CopyOnWrite 类的集合顾名思义,会在该变集合的操作中拷贝原数据,并用新的内部集合对象替换原内部对象。
  • Blocking 类的集合则通过锁(ReentrantLock)实现。它们会提供 “等待性” 的方法。

Concurrent 的代价

虽然 Concurrent 类的集合没有 CopyOnWrite 那么中的修改开销,但这是有代价的:

  • Concurrent 集合的遍历一致性较弱
    在利用迭代器遍历时,如果容器发生修改,迭代器可以继续进行遍历,不会抛出 ConcurrentModificationException。
    在 HashMap 中则会抛出此异常(也就是 fail-fast 机制)
  • 因为是弱一致性,所以 size 等操作未必准确
  • 读取的性能也具有不确定性

 

List

CopyOnWriteArrayList

CopyOnWrite 是指对该集合的任何修改操作都会:拷贝原数组,修改后替换原数组;以此达到线程安全的目的。
这种数据结构适合 读多写少 的场景。因为修改开销比较大。

可直接查看其 add() 方法了解实现原理:

public boolean add(E e) {
  final ReentrantLock lock = this.lock;
  lock.lock();
  try {
    Object elements = getArray();
    int len = elements.length;
    // 拷贝原数组
    Object[] newElements = Array.copyOf(elements, len + 1);
    newElements[len] = e;
    // 替换原数组(引用)
    setArray(newElements);
    return true;
  } finally {
    lock.unlock();
  }
}

 

Map

ConcurrentHashMap vs ConcurrentSkipListMap

  • ConcurrentHashMap 的存取速度更快
  • ConcurrentSkipListMap 的元素是经过排序的(它实现了接口 SortedMap)

为什么 HashMap 有对应的 ConcurrentHashMap,而 TreeMap 没有对应的 ConcurrentTreeMap ?
TreeMap 也实现了接口 SortedMap,它的元素也是经过排序的,为什么又造出一个 ConcurrentSkipListMap?
因为 TreeMap 内部基于复杂的红黑树,很难在并发场景中实现进行合理粒度的同步
SkipList 的内部结构更简单,实现增删元素时的线程安全开销更小;但是会占用更多空间

 

Set

不存在的 ConcurrentHashSet

不知道为啥没有一个对应的 ConcurrentHashSet。
虽然可以通过JDK自带的 Collections.newSetFromMap() 方法创建一个基于 ConcurrentHashMap 的线程安全 Set。
但是一般来说有一个对称的语义是比较好的设计。HashMap - HashSet, ConcurrentHashMap - ConcurrentHashSet。

Guava 中的 Sets.newConcurrentHashSet() 内部就是通过上述方式实现的:

public static  Set newConcurrentHashSet() {
  return Collections.newSetFromMap(new ConcurrentHashMap<E, Boolean>());
}

ConcurrentSkipListSet

就像 HashSet 和 HashMap 的关系,ConcurrentSkipListSet 内部用 ConcurrentSkipListMap 实现(value 为 Boolean.TRUE)

CopyOnWriteArraySet

CopyOnWriteArraySet 内部用 CopyOnWriteArrayList 实现。
适用于数据量很少,且读操作远远多于写操作的情况。在遍历的时候应避免多个线程操作其引用。

 

我想要线程安全的 LinkedHashMap

LinkedHashMap 经常被用于实现 LRU缓存(Least Recently Used)。
很遗憾的是JDK中没有对应的线程安全实现。对性能要求高的场景中,Collections.synchronizedMap() 之类的外层一刀切式同步方案又不合适。
这时可以考虑一个第三方的实现方案:guava 的 CacheBuilder。例:

Map<String, String> cache = CacheBuilder.newBuilder()
    .maximumSize(100)
    .<String, String>build()
    .asMap();

 

POM依赖:

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>28.0-jre</version>
</dependency>

 

分享到:
评论

相关推荐

    Java面试题合集最新版2024.zip

    集合框架:熟悉Java集合框架中的List、Set、Map等接口及其实现类,如ArrayList、HashSet、HashMap等。 泛型:理解泛型的概念及其在Java中的应用,如泛型类和泛型方法。 并发编程:了解Java中的线程、同步、锁等机制...

    Java面试技术面知识扩展包第一弹

    3. 集合框架:面试官可能会考察您对Java集合框架的理解和使用,包括List、Set、Map等不同类型的集合接口和对应的实现类,以及它们的特点和适用场景。 4. 多线程:面试官可能会提问关于多线程编程的问题,如线程的...

    java基础案例与开发详解案例源码全

    2.6 Java技术两种核心运行机制29 2.7 上机练习30 第3章 3.1 变量32 3.1.1 什么是变量32 3.1.2 为什么需要变量32 3.1.3 变量的声明和赋值33 3.1.4 变量应用实例33 3.2 数据的分类34 3.2.1 Java中的八种基本数据类型34...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

    第1章 Java应用分层架构及软件模型  1.1 应用程序的分层体系结构  1.1.1 区分物理层和逻辑层  1.1.2 软件层的特征  1.1.3 软件分层的优点  1.1.4 软件分层的缺点  1.1.5 Java应用的持久化层  1.2 软件的模型 ...

    javaSE代码实例

    3.10 常用数学工具包——java.lang.Math类 39 3.10.1 数学常量 39 3.10.2 常用数学函数 40 3.11 小结 41 第4章 流程控制——Java世界的航行舵手 42 4.1 if条件语句 42 4.1.1 简略形式 42 4.1.2 完全...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part4

    第1章 Java应用分层架构及软件模型  1.1 应用程序的分层体系结构  1.1.1 区分物理层和逻辑层  1.1.2 软件层的特征  1.1.3 软件分层的优点  1.1.4 软件分层的缺点  1.1.5 Java应用的持久化层  1.2 软件的模型 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part3

    第1章 Java应用分层架构及软件模型  1.1 应用程序的分层体系结构  1.1.1 区分物理层和逻辑层  1.1.2 软件层的特征  1.1.3 软件分层的优点  1.1.4 软件分层的缺点  1.1.5 Java应用的持久化层  1.2 软件的模型 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part1.rar

    第1章 Java应用分层架构及软件模型  1.1 应用程序的分层体系结构  1.1.1 区分物理层和逻辑层  1.1.2 软件层的特征  1.1.3 软件分层的优点  1.1.4 软件分层的缺点  1.1.5 Java应用的持久化层  1.2 软件的模型 ...

    NHibernate中文帮组文档(2008.11月更新)

    5.1.17. map, set, list, bag 5.1.18. 引用(import) 5.2. NHibernate 的类型 5.2.1. 实体(Entities)和值(values) 5.2.2. 基本值类型 5.2.3. 自定义值类型 5.2.4. Any类型映射 5.3. SQL中引号包围的标识符 5.4. 模块...

    net学习笔记及其他代码应用

    29.根据线程安全的相关知识,分析以下代码,当调用test方法时i&gt;10时是否会引起死锁?并简要说明理由。 public void test(int i) { lock(this) { if (i&gt;10) { i--; test(i); } } } 答:不会发生死锁,(但...

    python入门到高级全栈工程师培训 第3期 附课件代码

    02 级联删除与set null 03 多表查询之连接查询 04 多表查询之复合查询与子查询 05 mysql之索引 第48章 01 python操作数据库pymysql 02 数据库之事务 03 mysql事务之savepoint 第49章 01 http协议之请求协议 02 ...

    NHibernate参考文档 2.0.0 chm

    5.1.17. map, set, list, bag 5.1.18. 引用(import) 5.2. NHibernate 的类型 5.2.1. 实体(Entities)和值(values) 5.2.2. 基本值类型 5.2.3. 自定义值类型 5.2.4. Any类型映射 5.3. SQL中引号包围的标识符 5.4. 模块...

    hibernate 教程

    map, set, list, bag 5.1.16. 引用(import) 5.2. Hibernate 的类型 5.2.1. 实体(Entities)和值(values) 5.2.2. 基本值类型 5.2.3. 持久化枚举(Persistent enum)类型 5.2.4. 自定义值类型...

    hibernate

    map, set, list, bag 5.1.16. 引用(import) 5.2. Hibernate 的类型 5.2.1. 实体(Entities)和值(values) 5.2.2. 基本值类型 5.2.3. 持久化枚举(Persistent enum)类型 5.2.4. 自定义值类型...

    2019千峰Python超详细入门教程(百度云盘分享).docx

    │ 千锋Python教程:01.python概述和工具的安装.mp4 │ 千锋Python教程:02.数据存储与二进制操作1.mp4 │ 千锋Python教程:03.数据存储与二进制操作2.mp4 │ 千锋Python教程:04.第一个Python程序与注释及输入输出....

Global site tag (gtag.js) - Google Analytics