问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

【多线程】线程安全集合类,ConcurrentHashMap实现原理

创作时间:
作者:
@小白创作中心

【多线程】线程安全集合类,ConcurrentHashMap实现原理

引用
1
来源
1.
https://cloud.tencent.com/developer/article/2506984

在多线程编程中,线程安全是一个非常重要的问题。本文将深入探讨Java中线程安全集合类的实现原理,重点介绍ConcurrentHashMap的优化策略。

线程安全集合类

在Java中,许多常用的集合类如ArrayListQueueHashMap等都是线程不安全的。而像VectorStackHashtable这样的线程安全集合类虽然内置了synchronized,但并不推荐使用,因为它们在任何操作下都会加锁,即使是单线程环境下也会产生不必要的性能开销。

解决方案

多线程环境使用顺序表

  1. 自己加锁

    通过给ArrayList套壳的方式,为关键方法添加锁,确保线程安全。

    public class MyArrayList<T> {
        private final ArrayList<T> list = new ArrayList<>();
        private final Object lock = new Object();
    
        public void add(T element) {
            synchronized (lock) {
                list.add(element);
            }
        }
    
        // 其他方法类似
    }
    
  2. 使用CopyOnWriteArrayList

    这种方式采用了"写时复制"的策略。当需要修改顺序表时,会先复制一份新的顺序表,然后在新表上进行修改,最后通过原子操作修改引用指向。这种方式避免了锁的使用,但对频繁修改和大数据量的场景不太适用。

多线程环境使用队列

  1. 自己加锁
  2. 使用BlockingQueue

多线程环境使用哈希表

HashMap是线程不安全的,而Hashtable虽然线程安全但性能不佳。更推荐使用ConcurrentHashMap,它通过以下方式优化了线程安全问题:

  1. 缩小锁的粒度

    ConcurrentHashMap为每个链表分配一个锁,而不是像Hashtable那样使用全局锁。这样即使多个线程操作不同链表,也不会产生锁冲突。

    每个链表就是一个桶,这种设计被称为"锁桶"。从Java 8开始,每个链表都有一把独立的锁。

  2. 充分使用CAS

    ConcurrentHashMap在某些操作中使用了CAS(Compare and Swap)原子操作,进一步减少了锁的使用。

  3. 优化扩容操作

    HashMap在扩容时会一次性完成所有元素的迁移,这可能导致性能瓶颈。而ConcurrentHashMap采用"化整为零"的策略,每次只迁移一部分元素,确保操作时间不会过长。

    在扩容过程中,同时存在新旧两份哈希表:

  • 插入操作:直接在新表上插入
  • 删除操作:新旧表都删除
  • 查找操作:需要在新旧表中都查找

总结

ConcurrentHashMap通过缩小锁粒度、使用CAS操作和优化扩容策略,实现了高性能的线程安全。这些优化策略不仅提高了并发性能,还避免了不必要的锁竞争,是多线程环境下处理哈希表的首选方案。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号