一、HashTable的内部存储结构
HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:
1、HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都是synchronized。
2、HashTable不允许有null值的存在。
在HashTable中调用put方法时,如果key为null,直接抛出NullPointerException。其它细微的差别还有,比如初始化Entry数组的大小等等,但基本思想和HashMap一样。
二、HashTable和ConcurrentHashMap的比较
ConcurrentHashMap是线程安全的HashMap的实现。同样是线程安全的类,它与HashTable在同步方面有什么不同呢?
synchronized关键字加锁的原理,其实是对对象加锁,不论你是在方法前加synchronized还是语句块前加,锁住的都是对象整体,但是ConcurrentHashMap的同步机制和这个不同,它不是加synchronized关键字,而是基于lock操作的,这样的目的是保证同步的时候,锁住的不是整个对象。事实上,ConcurrentHashMap可以满足concurrentLevel个线程并发无阻塞的操作集合对象。
1、构造方法
为了容易理解,我们先从构造函数说起。ConcurrentHashMap是基于一个叫Segment数组的,其实和Entry类似,如下:
1
2
3
4
|
public ConcurrentHashMap()
{
this ( 16 , 0 .75F, 16 );
}
|
默认传入值16,调用下面的方法:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public ConcurrentHashMap ( int paramInt 1 , float paramFloat , int paramInt 2 )
{
if ( ( paramFloat < = 0 F ) || ( paramInt 1 < 0 ) || ( paramInt 2 < = 0 ) )
throw new IllegalArgumentException ( ) ;
if ( paramInt 2 > 65536 ) {
paramInt 2 = 65536 ;
}
int i = 0 ;
int j = 1 ;
while ( j < paramInt 2 ) {
+ + i;
j < < = 1 ;
}
this.segmentShift = ( 32 - i ) ;
this.segmentMask = ( j - 1 ) ;
this.segments = Segment.newArray ( j ) ;
if ( paramInt 1 > 1073741824 )
paramInt 1 = 1073741824 ;
int k = paramInt 1 / j;
if ( k * j < paramInt 1 )
+ + k;
int l = 1 ;
while ( l < k )
l < < = 1 ;
for ( int i 1 = 0 ; i 1 < this.segments.length; + + i 1 )
this.segments[i 1 ] = new Segment ( l , paramFloat ) ;
}
|
你会发现比HashMap的构造函数多一个参数,paramInt1就是我们之前谈过的initialCapacity,就是数组的初始化大小,paramfloat为loadFactor(装载因子),而paramInt2则是我们所要说的concurrentLevel,这三个值分别被初始化为16,0.75,16,经过:
1
2
3
4
|
while (j < paramInt2) {
++i;
j <<= 1 ;
}
|
后,j就是我们最终要开辟的数组的size值,当paramInt1为16时,计算出来的size值就是16.通过:
this.segments = Segment.newArray(j)后,我们看出了,最终稿创建的Segment数组的大小为16.最终创建Segment对象时:
1
|
this .segments[i1] = new Segment(cap, paramFloat);
|
需要cap值,而cap值来源于:
1
2
3
4
5
6
|
int k = paramInt1 / j;
if (k * j < paramInt1)
++k;
int cap = 1 ;
while (cap < k)
cap <<= 1 ;
|
组后创建大小为cap的数组。最后根据数组的大小及paramFloat的值算出了threshold的值:
this.threshold = (int)(paramArrayOfHashEntry.length * this.loadFactor)。