盒子
盒子
文章目录
  1. 线程同步
  2. put
  3. fail-fast 迭代器

Hashtable

PS : Hashtable,”table” 是小写开头啊!!源码白看了…

Hashtable 和 HashMap 原理是一致的,HashMap看这里.

HashMap 源码分析

两者直观的不同之处如下.

Hashtable 线程安全,键值不能为 null.HashMap 反之…

HashMap 也可以通过下面的语句进行同步:

1
Map m = Collections.synchronizeMap(hashMap);

Hashtable 在单线程环境下比 HashMap 要慢.

如果不需要同步(只需要单一线程),HashMap 性能优于 Hashtable.

为什么呢…

追溯到线程同步…

线程同步

线程同步使用的是 synchronized 关键字.

例如:

1
2
3
4
5
6
public synchronized V put(K key, V value) {
...
}
public synchronized V remove(Object key) {
...
}

具体看多线程相关…

直觉思考,保证线程同步,一定会需要一些开销…

在无需同步的情况下,Hashtable 仍会执行某些判断(比如执行一个操作来判断某个变量是否正被别人使用…),虽然只有真正需要同步时才会执行判断后的同步逻辑.

所以若无需同步,HashMap 性能优于 Hashtable.(因为它不必执行那些判断…)

put

查看源码,发现 Hashtable 没有 HashMap 中类似

1
2
3
4
5
6
7
8
/**
* JDK1.8 新加,Entry链表最大长度,当桶中节点数目大于该长度时,将链表转成红黑树存储;
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* JDK1.8 新加,当桶中节点数小于该长度,将红黑树转为链表存储;
*/
static final int UNTREEIFY_THRESHOLD = 6;

的属性,查看 put 方法,发现也没有使用到红黑树结构.

Hashtable 比之 HashMap,没有使用红黑树,把新插入的元素放入 Entry 这个链表最前边.

fail-fast 迭代器

看源码,发现还有个 HashMap 没见过的属性.

1
2
3
4
5
6
7
8
 /**
* The number of times this Hashtable has been structurally modified
* Structural modifications are those that change the number of entries in
* the Hashtable or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the Hashtable fail-fast. (See ConcurrentModificationException).
*/
private transient int modCount = 0;

modCount 用来实现 fail-fast 机制…

这个属性在 Hashtable 很显眼,之前在 HashMap 没注意…

啥是 fail-fast 机制啊?

fail-fast 机制是 java 集合的一种错误处理机制,下面这些类都有…

Map: HashMap,LinkedHash,MapTree,Map,Hashtable(线程安全)

Set: HashSet,LinkedHashSet,TreeSet

List: ArrayList,LinkedList,Vector(线程安全)

Collections.synchronizedXXX() 创建的线程安全的集合

具体是啥?

比如当多个线程对集合进行操作时,一个线程通过 itertor 遍历集合,而此刻另一个线程改变了集合的内容,这时迭代器会立马感知,立即抛出 ConcurrentModificationException 异常.

fail-fast 机制只能用于检测错误.

如何处理呢?

若在多线程环境下使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”。

搜索了一下,发现 modCount 几乎无处不在,put() 时会更新,clear() 时会更新…

查看源码,发现有这样的操作.

1
2
if (modCount != expectedModCount)
throw new ConcurrentModificationException();

不难想象,fail-fast 利用 modCount 来实现.比如当一个线程对集合进行遍历,在此线程保持 expectedModCount =modCount,此时另一线程对集合进行 put() 操作,modCount++.

判断 modCount != expectedModCount,立马抛出异常…

删除,插入元素都会影响 modCount,这叫做结构上的更改…

PS : 然而 set() 操作不会被视为结构性的修改.