网站备案未注销 影响网站如何优化推广
一、基本思想
基于HASH表的并发map(map怎么翻译,感觉叫E文更加通用点吧)。有跟java.lang.HashMap一样的方法,但是每个方法都是线程安全的。
建议对HashMap了解之后再看这篇文章,关于HashMap本人之前写过一篇博客来说明
http://tangmingjie2009.iteye.com/blog/1698595
二、源码解析
2.1 基本常量
/*** The default initial capacity for this table,* used when not otherwise specified in a constructor.*/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;//默认并发级别,用在构造方法中/*** The maximum capacity, used if a higher value is implicitly* specified by either of the constructors with arguments. MUST* be a power of two <= 1<<30 to ensure that entries are indexable* using ints.*/static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量/*** 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;//最小段容量/*** The maximum number of segments to allow; used to bound* constructor arguments. Must be power of two less than 1 << 24.*/static final int MAX_SEGMENTS = 1 << 16; // slightly conservative//最大段/*** 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;//上锁前的重试次数
2.2 内部类
类中有三个内部类
private static class Holder;
//JVM 启动的时候加载jdk.map.althashing.threshold值
static final class HashEntry<K,V>;
//永远不被暴露出去的Key value实体
static final class Segment<K,V> extends ReentrantLock implements Serializable;
//将ReentrantLock 和Entry<K,V>结合到一起的东西
2.3 get
public V get(Object key) {Segment<K,V> s; // manually integrate access methods to reduce overheadHashEntry<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;}
这个比较简单,因为不牵涉到节点的删除,如果把下面的put看懂之后,理解就更加容易些了。
2.4 put
public V put(K key, V value) {Segment<K,V> s;if (value == null)//值不为空throw new NullPointerException();int hash = hash(key);//获得hashCodeint j = (hash >>> segmentShift) & segmentMask; if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegments = ensureSegment(j);//根据key的hash值计算看是否有个节点,如果没有就创建一个return s.put(key, hash, value, false);//将key,value放入这个节点中}
要知道segmentShift 和segmentMash 可以从构造方法来看
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 argumentsint sshift = 0;int ssize = 1;while (ssize < concurrencyLevel) {++sshift;ssize <<= 1;}this.segmentShift = 32 - sshift;this.segmentMask = ssize - 1;。。。。。。后面省略
按照无参的构造方法来看的话,三个值分别是16,0.75,16,这样如果算下来segmentShift=28,segmentMask=15。
那么它是怎么存放的呢,看下面的方法,这里有重入锁的概念了
final V put(K key, int hash, V value, boolean onlyIfAbsent) {HashEntry<K,V> node = tryLock() ? null :scanAndLockForPut(key, hash, value);//如果锁失败的话调用这个方法,//里面是一个循环去尝试锁的逻辑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) {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);elsenode = new HashEntry<K,V>(hash, key, value, first);int c = count + 1;if (c > threshold && tab.length < MAXIMUM_CAPACITY)rehash(node);elsesetEntryAt(tab, index, node);++modCount;count = c;oldValue = null;break;}}} finally {unlock();}return oldValue;}
2.5 遍历
通过迭代器来实现遍历的,对于所有的map来说通常都是这样
Iterator<Entry<Integer, String>> iterator = map.entrySet().iterator();while(iterator.hasNext()){Entry<Integer, String> next = iterator.next();next.getKey();next.getValue();}
三、适用范围
源代码的文档的解释:
这个类和它的视图和迭代器实现了所有的Map 和Interator的接口。相比较而言更像HashTable而不想HashMap,因为它不允许空值作为key和value.
个人理解:
多线程编程中非常常用的一个类。不会出现HashMap中ConcurrentModificationException。可以不加锁的情况下放心使用。
四、测试