随笔,map集合


HashMap中未进行同步考虑,而 Hashtable则使用了synchronized,在每个操作hash表的方法上加上了synchronized同步锁例如:

public synchronized int size() {
return count;
}
public synchronized boolean isEmpty() {
return count == 0;
}

public synchronized boolean contains(Object value) {
if (value == null) {
throw new NullPointerException();
}

Entry tab[] = table;
for (int i = tab.length ; i-- > 0 ;) {
for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}

public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}

所以我们可以在单线程时使用HashMap提高效率,而多线程时用 Hashtable来保证安全。

但是,通过分析Hashtable可以发现,synchronized锁住的是整张Hash表,也就是说在多个线程访问Hash表对他进行操作的时候
只允许单个线程进行操作,这造成了极大的浪费。
再看SynchronizedMap<K,V> : jdk1.7

private final Map<K,V> m; // Backing Map
final Object mutex;

SynchronizedMap(Map<K,V> m) { //构造方法 将自身作为锁对象
if (m==null)
throw new NullPointerException();
this.m = m;
mutex = this;
}

SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
this.mutex = mutex;
}
//直接锁对象 将自己整个实例对象作为同步资源
public int size() {
synchronized (mutex) {return m.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return m.isEmpty();}
}
public boolean containsKey(Object key) {
synchronized (mutex) {return m.containsKey(key);}
}
......
类似于Hashtable (锁方法和锁对象 锁方法:在调用非静态方法时候是通过了对象调用方法那么锁的资源应该是this对象本身,调用静态方法是通过类直接调用那么锁住的是类的字节码文件?)


ConcurrentHashMap: ConcurrentHashMap的最大改进就是将粒度细化到了桶上

几个重要的内部类:
//Hash表节点类
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K,V> next;

volatile关键字的作用:保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。


为什么会出现脏读?

Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。变量的值何时从线程的工作内存写回主存,无法确定。

HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}

/**
* Sets next field with volatile write semantics. (See above
* about use of putOrderedObject.)
*/
final void setNext(HashEntry<K,V> n) {
UNSAFE.putOrderedObject(this, nextOffset, n);
}

// Unsafe mechanics
static final sun.misc.Unsafe UNSAFE;
static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = HashEntry.class;
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}

//桶类,其实就是一个大的hash表的子表 (只罗列重要的字段和方法)

static final class Segment<K,V> extends ReentrantLock implements Serializable {
//存储hash表
transient volatile HashEntry<K,V>[] table;
//实际桶内元素的数量
transient int count;

//put方法
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value); //争取锁,争取不到就调用scanAndLockForPut定时执行tryLock方法竞争锁
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);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}

 

ConcurrentHahsMap:的get方法

//get方法,这里应为jdk1.5以后的版本添加了volatile关键字,不必在为空的时候再重复读了
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;
}

 


posted @ 2021-10-10 22:41  timelfb  阅读(31)  评论(0)    收藏  举报