《Java 核心技术面试》课程笔记(十)

如何保证集合是线程安全的?

典型回答

  • Java 提供了不同层⾯的线程安全支持。
    • 在传统集合框架内部,除了 Hashtable 等同步容器,还提供了所谓的同步包装器(Synchronized Wrapper),我们可以调用 Collections 工具类提供的包装方法,来获取⼀个同步的包装容器(如 Collections.synchronizedMap),但是它们都是利用非常粗粒度的同步方式,在高并发情况下,性能比较低下。
    • 普遍的选择是利用并发包提供的线程安全容器类:
      • 各种并发容器,比如 ConcurrentHashMap、CopyOnWriteArrayList。
      • 各种线程安全队列(Queue/Deque),如 ArrayBlockingQueue。
      • 各种有序容器的线程安全版本等。
    • 具体保证线程安全的方式,包括有从简单的 synchronize 方式,到基于更加精细化的,比如基于分离锁实现的 ConcurrentHashMap 等并发实现等。

考点分析

  • 谈到线程安全和并发,可以说是 Java 面试中必考的考点。
  • 如果要深入思考并回答这个问题及其扩展方面,至少需要:
    • 理解基本的线程安全工具。
    • 理解传统集合框架并发编程中 Map 存在的问题,清楚简单同步方式的不足。
    • 梳理并发包内,尤其是 ConcurrentHashMap 采取了哪些方法来提高并发表现。
    • 最好能够掌握 ConcurrentHashMap 自身的演进。

知识扩展

  • 为什么需要 ConcurrentHashMap?
    • Hashtable 本身比较低效,因为它的实现基本就是将 put、get、size 等各种方法加上 “synchronized”。
    • 简单来说,这就导致了所有并发操作都要竞争同⼀把锁,⼀个线程在进行同步操作时,其他线程只能等待,大大降低了并发操作的效率。
    • Hashtable 或者同步包装版本,都只是适合在非高度并发的场景下。
  • ConcurrentHashMap 分析
    • 早期 ConcurrentHashMap,其实现是基于:
      • 分离锁,也就是将内部进行分段(Segment),里面则是 HashEntry 的数组,和 HashMap 类似,哈希相同的条目也是以链表形式存放。
      • HashEntry 内部使用 volatile 的 value 字段来保证可见性,也利用了不可变对象的机制以改进利用 Unsafe 提供的底层能力,比如 volatile access,去直接完成部分操作,以最优化性能,毕竟 Unsafe 中的很多操作都是 JVM intrinsic 优化过的。
      • 核心是利用分段设计,在进行并发操作的时候,只需要锁定相应段,这样就有效避免了类似 Hashtable 整体同步的问题,大大提高了性能。
      • 在构造的时候,Segment 的数量由所谓的 concurrencyLevel 决定,默认是 16,也可以在相应构造函数直接指定。注意,Java 需要它是 2 的幂数值,如果输入是类似 15 这种非幂值,会被自动调整到 16 之类 2 的幂数值。
        在这里插入图片描述
    • 在 Java 8 和之后的版本中,ConcurrentHashMap 发生了哪些变化呢?
      • 总体结构上,它的内部存储变得和 HashMap 结构非常相似,同样是大的桶(bucket)数组,然后内部也是⼀个个链表结构(bin),同步的粒度要更细致⼀些。
      • 其内部仍然有 Segment 定义,但仅仅是为了保证序列化时的兼容性而已,不再有任何结构上的用处。
      • 因为不再使用 Segment,初始化操作大大简化,修改为 lazy-load 形式,这样可以有效避免初始开销。
      • 数据存储利⽤ volatile 来保证可见性。
      • 使用 CAS 等操作,在特定场景进行无锁并发操作。
      • 使用 Unsafe、LongAdder 之类底层手段,进行极端情况的优化。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值