简单学习ConcurrentHashMap实现线程安全的原理

本文深入探讨了Java中解决多线程数据安全问题的方法,重点介绍了ConcurrentHashMap的设计原理及其在不同JDK版本间的演变,包括从Java 1.6到Java 1.8的重要改进。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  提到多线程肯定想到数据的线程安全问题如何解决,util包中的Hashtable,Vector都是线程安全的,最初的时候也都会选择这几种数据存储方式,在前几年面试的时候也经常会被问到Hashtable与HashMap,Vector和ArrayList的区别。简单看一下Hashtable,Vector线程安全的实现方式,这两种都是直接对方法加synchronized,直接上代码,看一下Hashtable。

public synchronized V get(Object key) { //方法内部代码不再贴了,与这次学的东西无关 } 
public synchronized V put(K key, V value) {} 

Vector的方式也是这样。个别没有加上synchronized关键字的public方法,也是调用了对象内部的同步方法实现了同步或者用Collections.synchronized方式加上同步,使该方法实现同步。除了这样实现线程安全,还可以直接使用Collections.SynchronizedCollection对其他非线程安全的数据结构加锁实现线程安全。
  不管是对方法加synchronized,还是使用Collections.SynchronizedCollection实现同步,对对象的修改或者查询时,锁住的都是整个对象。 上面的实现方式确实解决了大部分线程安全问题,但随之而来的确实性能的下降。举个例子:用一个Hashtable存储了用户信息,每当一个用户有修改甚至查询请求时,整个Hashtable就要被锁住,也就是说,在这个玩家信息数据处理期间,所有用户的相关请求线程全部要等待,用户量级小的时候也许感觉不出来卡顿,如果放在当前互联网环境下,那将是灾难性的。
  进入正题,Java1.5之后加入了java.util.concurrent 包,这个包包含有一系列能够让 Java 的并发编程变得更加简单轻松的类。在这个包被添加以前,你需要自己去动手实现能够解决以上问题的工具包。这次主要学习一下ConcurrentHashMap的实现原理。
  ConcurrentHashMap定义了Segment内部类,看一下代码:

//Segment继承了ReentrantLock重入锁(这个概念这次先不看)
static final class Segment<K,V> extends ReentrantLock implements Serializable {
    //HashEntry与HashMap中类似,可以理解为一个单向链表元素,作为存放相同hash值不同key的键值对
    //这样一个Segment就相当于一个HashMap
    transient volatile HashEntry<K,V>[] table;
    V put(K key, int hash, V value, boolean onlyIfAbsent) {
        //在对Segment进行操作时,对当前对象加锁
        lock();
        try {
        //数据操作
        } finally {
           unlock();
        }
    }

}

ConcurrentHashMap通过数组形式存放多个Segment,用key的hash值做一次再hash当做下标识别当前键值对存放在哪个segment里。

  final Segment<K,V>[] segments;
  public V put(K key, V value) {
        if (value == null)
            throw new NullPointerException();
        //用key的hashCode再做一次hash
        int hash = hash(key.hashCode());
        return segmentFor(hash).put(key, hash, value, false);
    }

在对segment元素进行操作时加锁,这样当其它人线程操作当前ConcurrentHashMap对象时,只要key的hash值与加锁的值不同,就可以直接操作其它Segment元素。
示意图:
这里写图片描述


以上原理皆为JDK1.6版本源码,JDK1.8对ConcurrentHashMap做了大量修改,不仅去除了Segment概念,直接采用HashEntry保存数据,将锁也直接加载HashEntry,大大减少了数据冲突引起的延迟,还引入了红黑树算法,使hash之后在数组的分布更加均匀。对JDK1.8的源码学习之后补上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值