文档章节

ConcurrentHashMap 锁分段 源码分析

w
 wannshan
发布于 2017/04/10 17:38
字数 1274
阅读 33
收藏 1

看ConcurrentHashMap下几个属性:

/**
     * The default concurrency level for this table, used when not
     * otherwise specified in a constructor.
     * 默认的分段锁个数
     */
    static final int DEFAULT_CONCURRENCY_LEVEL = 16;

     /**
     * The minimum capacity for per-segment tables.  Must be a power
     * of two, at least two to avoid immediate resizing on next use
     * after lazy construction. 每个分段锁,最小容量
     */
    static final int MIN_SEGMENT_TABLE_CAPACITY = 2;


     /**
     * 尝试获取锁的次数
     * Number of unsynchronized retries in size and containsValue
     * methods before resorting to locking. This is used to avoid
     * unbounded retries if tables undergo continuous modification
     * which would make it impossible to obtain an accurate result.
     */
    static final int RETRIES_BEFORE_LOCK = 2;

    /**
     * The segments, each of which is a specialized hash table.
     * 分段锁数组 
     */
    final Segment<K,V>[] segments;

 

/**
     * Segments are specialized versions of hash tables.  This
     * Segments 也是一个定制的hashtable
     * subclasses from ReentrantLock opportunistically, just to
     * 同时他也是 ReentrantLock的子类
     * simplify some locking and avoid separate construction.
     */
    static final class Segment<K,V> extends ReentrantLock implements Serializable {
    .....
    }
  //以put为例分析

 /**
     * Maps the specified key to the specified value in this table.
     * Neither the key nor the value can be null.
     * key 和 value 都不能为null 否则抛异常
     * <p> The value can be retrieved by calling the <tt>get</tt> method
     * with a key that is equal to the original key.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
     * @throws NullPointerException if the specified key or value is null
     */
    @SuppressWarnings("unchecked")
    public V put(K key, V value) {
        Segment<K,V> s;
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key);//先通过key求hash,再获取当前hash在哪个分段锁内,这些全是位操作,比较烦,也能分析透,还有UNSAFE类的使用比较多。UNSAFE是高危操作类,但是高效,功能强大。
        int j = (hash >>> segmentShift) & segmentMask;
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);//没找到合适分段,调用ensureSegment() 看下面,
        return s.put(key, hash, value, false);//调用的分段锁Segment的put方法
    }


    /**
     * Returns the segment for the given index, creating it and
     * recording in segment table (via CAS) if not already present.
     *  返回给定的索引的分段,不存在就创建一个。
     * @param k the index
     * @return the segment
     */
    @SuppressWarnings("unchecked")
    private Segment<K,V> ensureSegment(int k) {
        final Segment<K,V>[] ss = this.segments;
        long u = (k << SSHIFT) + SBASE; // raw offset
        Segment<K,V> seg;
        if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {//通过索引没取到分段,创建分段
            Segment<K,V> proto = ss[0]; // use segment 0 as prototype  //总是以ss[0]为例 这点写死了
            int cap = proto.table.length;
            float lf = proto.loadFactor;
            int threshold = (int)(cap * lf);
            HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap];
            if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
                == null) { // recheck//再检查一次,有没有合适的分段
                Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);//创建分段
                while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
                       == null) { //再检查一次,有没有合适的分段  第三次检查,
                    if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))//最后用cas方法,把新建的分段放到分段数组中
                        break;
                }
            }
        }
        return seg;
    }

    //  关键看下Segment类 
      /**
     * Segments are specialized versions of hash tables.  This 
     * subclasses from ReentrantLock opportunistically, just to
     * simplify some locking and avoid separate construction.
     * 它是ReentrantLock的子类
     */
      static final class Segment<K,V> extends ReentrantLock implements Serializable {}
	
     //看下Segment的put方法
      final V put(K key, int hash, V value, boolean onlyIfAbsent) {
            HashEntry<K,V> node = tryLock() ? null :
                scanAndLockForPut(key, hash, value);//先用tryLock()获取锁,如果成功node=null ,否则进入scanAndLockForPut(key, hash, value)方法,看下面scanAndLockForPut方法
            V oldValue;
            try {
                HashEntry<K,V>[] tab = table;
                int index = (tab.length - 1) & hash;
                HashEntry<K,V> first = entryAt(tab, index);
                for (HashEntry<K,V> e = first;;) {
                    if (e != null) {//key 的hash位置有值了
                        K k;
                        if ((k = e.key) == key ||
                            (e.hash == hash && key.equals(k))) {
                            oldValue = e.value;
                            if (!onlyIfAbsent) {
                                e.value = value;
                                ++modCount;
                            }
                            break;
                        }
                        e = e.next;
                    }
                    else {
                        if (node != null)
                            node.setNext(first);
                        else
                            node = new HashEntry<K,V>(hash, key, value, first);
                        int c = count + 1;
                        if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                            rehash(node);//扩容 *2的大小
                        else
                            setEntryAt(tab, index, node);。
                        ++modCount;
                        count = c;
                        oldValue = null;
                        break;
                    }
                }
            } finally {
                unlock();//释放锁
            }
            return oldValue;
        }


	  /**
         * Scans for a node containing given key while trying to
         * acquire lock, creating and returning one if not found. Upon
         * return, guarantees that lock is held. UNlike in most
         * methods, calls to method equals are not screened: Since
         * traversal speed doesn't matter, we might as well help warm
         * up the associated code and accesses as well.
         *  通过key找对应node.没有就创建一个。
         * @return a new node if key not found, else null
         */
        private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
            HashEntry<K,V> first = entryForHash(this, hash);
            HashEntry<K,V> e = first;
            HashEntry<K,V> node = null;
            int retries = -1; // negative while locating node
            while (!tryLock()) {//没有获取锁,重试
                HashEntry<K,V> f; // to recheck first below
                if (retries < 0) {
                    if (e == null) {
                        if (node == null) // speculatively create node
                            node = new HashEntry<K,V>(hash, key, value, null);
                        retries = 0;
                    }
                    else if (key.equals(e.key))
                        retries = 0;
                    else
                        e = e.next;
                }
                else if (++retries > MAX_SCAN_RETRIES) {//大于最大尝试次数
                    lock();//等待,阻塞锁
                    break;
                }
                else if ((retries & 1) == 0 &&
                         (f = entryForHash(this, hash)) != first) {
                    e = first = f; // re-traverse if entry changed
                    retries = -1;
                }
            }
            return node;
        }

     /**  get 方法不加锁
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code key.equals(k)},
     * then this method returns {@code v}; otherwise it returns
     * {@code null}.  (There can be at most one such mapping.)
     *
     * @throws NullPointerException if the specified key is null
     */
    public V get(Object key) {
        Segment<K,V> s; // manually integrate access methods to reduce overhead
        HashEntry<K,V>[] tab;
        int h = hash(key);
        long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
        if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
            (tab = s.table) != null) {
            for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
                     (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
                 e != null; e = e.next) {
                K k;
                if ((k = e.key) == key || (e.hash == h && key.equals(k)))
                    return e.value;
            }
        }
        return null;
    }

 

© 著作权归作者所有

共有 人打赏支持
w
粉丝 19
博文 42
码字总数 79122
作品 0
浦东
技术主管
私信 提问
ConcurrentHashMap源码分析

前言 JDK中的Hashtable是一个线程安全的K-V形式的容器,它实现线程安全的原理十分简单,就是在所有涉及对该哈希表操作的方法上都加上了synchronized关键字,进行加锁操作。这么做实现了线程安...

Justlearn
2017/04/13
0
0
探究Java的ConcurrentHashMap实现机制

原文地址: http://blog.csdn.net/u011080472/article/details/51392712 在学习ConcurrentHashMap的高并发时,找到了一些高质量的博客,就没有重复转载了。分别列出了JDK6中的Segment分段加锁...

Gavin__Zhou
2017/08/06
0
0
ConcurrentHashMap源码分析(JDK1.7和JDK1.8)

ConcurrentHashMap是Java中使用非常普遍的一个Map,ConcurrentHashMap较HashMap而言极大提高了并发操作速度,我们知道HashMap是线程不安全的,在多线程环境下容易出现死锁,线程安全的HashT...

申文波
07/04
0
0
ConcurrentHashMap源码解析

本文主要内容 ConcurrentHashMap介绍 ConcurrentHashMap初始化 ConcurrentHashMap存储流程 ConcurrentHashMap取出流程 总结 1、ConcurrentHashMap介绍 关于Java集合类,已经介绍过很多了,今...

某昆
08/18
0
0
ConcurrentHashMap实现原理及源码分析

ConcurrentHashMap实现原理及源码分析   ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考 另一篇文章HashMap实现原理及...

DemonsI
08/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Nginx/Apache/PHP Hidden Version Number

nginx隐藏版本号的方法 在nginx配置文件nginx.conf中,加入以下代码: server_tokens off; apache隐藏版本号的方法 在apache配置文件httpd.conf中,加入以下代码: ServerTokens ProdServer...

Linux就该这么学
3分钟前
1
0
iOS 滚动视图 UIScrollView 代理方法 相关属性 状态栏 滚动条 回弹 弹簧效果

iOS 滚动视图 UIScrollView 代理方法 相关属性 状态栏 滚动条 回弹 弹簧效果 #import "ViewController.h"@interface ViewController () <span style="color:#ff0000;"><UIScrollViewDel......

壹峰
9分钟前
0
0
【转】Hive 工作原理详解

什么是Hive? Hive是建立在 Hadoop 上的数据仓库基础构架。它提供了一系列的工具,可以用来进行数据提取转化加载(ETL),这是一种可以存储、查询和分析存储在 Hadoop 中的大规模数据的机制。...

hnairdb
9分钟前
1
0
开源 java CMS - FreeCMS2.8 微信管理 公众号设置

项目地址:http://www.freeteam.cn/ 公众号设置 管理员可以在这里设置微信公众号的相关配置,每个站点都可设置自己的微信公众号。 其中接口URL需要配置在微信公众号中,如下图所示 App Key ...

freeteam
11分钟前
1
0
MySQL中 replace 函数应用

将以下字段中的【忻州】替换为【洛阳】 Sql语句如下 UPDATE sys_organization SET org_name = REPLACE(org_name,'忻州','洛阳'); 执行结果如下...

karma123
13分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部