ConcurrentHashMap详解

ConcurrentHashMap详解

本文旨在从源码层面分析ConsurrentHashMap的原理

为什么说ConcurrentHashMap是线程安全的呢
  1. 为什么会有线程安全问题
    因为多线程情况下操作同一个资源,因为操作不是原子性的,导致出现脏写等情况
    拿HashMap举例来说,下图是他的put方法,逻辑是我们常用的,先判断元素是否存在,不存在就设置值
    在这里插入图片描述
    在这里插入图片描述
  2. ConcurrentHashMap是怎么解决的
    首先put方法是一个for无限循环,完成后才会退出
    当put数据时,计算出hashmod值后
    1. 底层数组该位置没有数据
      在这里插入图片描述
      使用cas的方式插入数据,如果成功,则直接返回,如果失败,继续外层循环,使用乐观锁 + CAS的方式,保证线程安全
    2. 当底层数组该位置已经有数据
      对该位置链表的头部元素加锁
      在这里插入图片描述
ConcurrentHashMap的get方法有加锁吗,为什么

从源码看,当key不存在或者value所在的数组位置为链表时,没有加锁,但是当value所在的数组位置为tree时,会执行find方法

public V get(Object key) {
        ConcurrentHashMap.Node<K,V>[] tab; ConcurrentHashMap.Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
                (e = tabAt(tab, (n - 1) & h)) != null) {
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
                if (e.hash == h &&
                        ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

find方法

	/**
 * Returns matching node or null if none. Tries to search
 * using tree comparisons from root, but continues linear
 * search when lock not available.
 */
final Node<K,V> find(int h, Object k) {
    if (k != null) {
        for (Node<K,V> e = first; e != null; ) {
            int s; K ek;
            // 如果当前读写锁的状态是WAITER或者WRITER,则通过链表遍历查找,此时红黑树正在调整的过程中。
            if (((s = lockState) & (WAITER|WRITER)) != 0) {
                if (e.hash == h &&
                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;
                e = e.next;
            }
            // //如果不是上述两种状态,则将状态设置为READER,每次都会加上READER,表示正在遍历红黑树,此时就不能调整红黑树了
            else if (U.compareAndSwapInt(this, LOCKSTATE, s,
                                         s + READER)) {
                TreeNode<K,V> r, p;
                try {
                    // 如果根节点为null,则返回null;否则通过根节点查找匹配的节点。注意:进入此方法根节点不可能为null
                    p = ((r = root) == null ? null :
                         r.findTreeNode(h, k, null));
                } finally {
                    Thread w;
                    if (U.getAndAddInt(this, LOCKSTATE, -READER) == //所有的读锁都释放了
                        (READER|WAITER) && (w = waiter) != null) //且有等待获取写锁的线程
                        // 唤醒该线程
                        LockSupport.unpark(w);
                }
                return p;
            }
        }
    }
    return null;
}
ConcurrentHashMap迭代器是强一致还是弱一致

是弱一致的,因为在迭代过程中,ConcurrentHashMap没有加锁,可以插入数据,而HashMap不能插入数据

		// HashMap
        final Node<K,V> nextNode() {
            Node<K,V>[] t;
            Node<K,V> e = next;
            // 元素个数不对,直接抛出错误
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值