ConcurrentHashMap的特性

来源:互联网 发布:windows update 在哪 编辑:程序博客网 时间:2024/06/06 12:41

1.ConcurrentHashMap写操作

ConcurrentHashMap采用了分段锁的设计,只有在同一个分段内才存在竞态关系,不同的分段锁之间没有锁竞争。相比于对整个Map加锁的设计,分段锁大大的提高了高并发环境下的处理能力。
       因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。

初始化:
ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>();
static final int DEFAULT_INITIAL_CAPACITY = 16;    /**     * The default load factor for this table, used when not     * otherwise specified in a constructor.     */    static final float DEFAULT_LOAD_FACTOR = 0.75f;    /**     * The default concurrency level for this table, used when not     * otherwise specified in a constructor.     */    static final int DEFAULT_CONCURRENCY_LEVEL = 16;


 public ConcurrentHashMap() {        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);    }

@SuppressWarnings("unchecked")    public ConcurrentHashMap(int initialCapacity,                             float loadFactor, int concurrencyLevel) {        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)            throw new IllegalArgumentException();        if (concurrencyLevel > MAX_SEGMENTS)            concurrencyLevel = MAX_SEGMENTS;        // Find power-of-two sizes best matching arguments        int sshift = 0;        int ssize = 1;        while (ssize < concurrencyLevel) {            ++sshift;            ssize <<= 1;        }        this.segmentShift = 32 - sshift;        this.segmentMask = ssize - 1;//ssize默认为16,也就是多少个分段        if (initialCapacity > MAXIMUM_CAPACITY)            initialCapacity = MAXIMUM_CAPACITY;        int c = initialCapacity / ssize;        if (c * ssize < initialCapacity)            ++c;        int cap = MIN_SEGMENT_TABLE_CAPACITY;        while (cap < c)            cap <<= 1;        // create segments and segments[0]        Segment<K,V> s0 =            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),                             (HashEntry<K,V>[])new HashEntry[cap]);        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];//这里new了一个分段数组并且初始化了s0        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]        this.segments = ss;    }




可以看出不同的key可能会有不同的段。UNSAFE.getObject(segments, (j << SSHIFT) + SBASE))

put操作的代码:
final V put(K key, int hash, V value, boolean onlyIfAbsent) {            HashEntry<K,V> node = tryLock() ? null :                scanAndLockForPut(key, hash, value);

重要的是tryLock()这个方法。当线程A进入该方法时。代码逻辑如下:
 final boolean nonfairTryAcquire(int acquires) {            final Thread current = Thread.currentThread();            int c = getState();            if (c == 0) {                if (compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }            else if (current == getExclusiveOwnerThread()) {                int nextc = c + acquires;                if (nextc < 0) // overflow                    throw new Error("Maximum lock count exceeded");                setState(nextc);                return true;            }            return false;        }

其中c的值为0,于是会将private volatile int state;该变量的值更新为1。如果此时线程B进入该方法那么结果将会
进入scanAndLockForPut方法。
 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()) {
如果tryLock返回false,也就是getstate()返回的值是1,相当于A线程还没有释放锁。将会一直处于循环。
直到A线程释放锁。
 } finally {                unlock();            }
 protected final boolean tryRelease(int releases) {            int c = getState() - releases;            if (Thread.currentThread() != getExclusiveOwnerThread())                throw new IllegalMonitorStateException();            boolean free = false;            if (c == 0) {                free = true;                setExclusiveOwnerThread(null);            }            setState(c);            return free;        }
unlock的操作实际上是吧state更新为0。于是线程B就脱离循环的,这就实现了写操作的同步。只是处于一个分段中。
不同的分段就不需要考虑这些了。总的来说就是一种分片加锁的思想。





原创粉丝点击