HashMap1.8与ConcurrentHashMap1.8线程安全比较

HashMap大家再熟悉不过了,它是java专门用来存储K-V类型的集合框架,它是线程不安全的,同时它的底层原理也是面试必问,但是ConcurrentHashMap大家对他了解多少呢?大家可能只知道它是线程安全的,但它的底层是怎么实现的呢?它在HashMap的基础之上做了什么优化呢?我今天就带大家来了解一下HashMap与ConcurrentHashMap的不同之处。


1、HashMap初始化时。

HashMap初始化时会调用一个resize()的方法,这个方法内部会判断Node数组有没有值来对数组初始化长度,或者进行数组扩容。

大家想一下,若是在单线程还好,新元素put进来之后,我判断数组有没有值,没有值就初始化长度为16,然后将这个元素放进去。这个过程一定是按顺序进行的。所以不会存在线程安全问题。

但是在多线程情况下呢?若两个线程同时去初始化,一个长度为16,另一个长度为32,这个过程会出现问题吗?或者说,第一个线程还没初始化完成,第二个线程已经开始去put元素了,这个过程会出现问题吗?这意味着在多线程的情况下操作可能会和单线程操作的情况数据不一致。这就出现了线程安全问题。

这时候怎么办呢?大家可能会想到加锁,使用synchronized关键字。没错,这的确是一个可行的办法,HashTable就是它最好的实现。大家可以取HashTable源码上看一看,大多数方法都使用了synchronized修饰。

我们这里就看一下它的put方法:

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }
    .....
    return null;
}

我们看到HashTable的put方法使用了synchronized 来修饰,确保了put元素时候线程的线程安全。但是使用synchronized 之后就只能一次执行一个线程了,这样的话,操作起来效率会很低。

这时候ConcurrentHashMap就出现了,它采用了一种无锁化的机制来解决这这个问题,实现就是Compare And Swap :CAS,属于乐观锁的一种。它可以保证对某个操作的线程安全。它是怎么保证的呢?--->它会去拿到对某个数据操作的内存当中的最新值以及你需要去修改的这个值,两者进行比较,如果说是相等的,就代表是安全的没有被改动过,如果不相等,就认为被其他线程改动过,这时候就不能再对它进行改动。

简单来说就是,比如说我有一个int a;它在内存中会有一个自己的值,这时候来了一个线程X把修改a为b,那么a的内存中的值就会变为b,若Z线程进来拿着a的值想要修改a为c,这时候a的值已经被修改为b了,a和b比较不对等,所以Z线程就不能进行操作了。这就是CAS的基本原理。使用它之后效率就会非常高。

接下老我们看一下HashMap与ConcurrentHashMap的数组初始化代码有什么不同:

HashMap的resize()方法:

/**
 * Initializes or doubles table size.  If null, allocates in
 * accord with initial capacity target held in field threshold.
 * Otherwise, because we are using power-of-two expansion, the
 * elements from each bin must either stay at same index, or move
 * with a power of two offset in the new table.
 * @return the table
 */
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
         
  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值