1. 方法层面的同步机制
- 大部分方法使用
synchronized
关键字:
Hashtable
中的许多关键方法,如 put
、get
、remove
等,都被声明为 synchronized
。以 put
方法为例,其大致实现如下:
public synchronized V put(K key, V value) {
// 检查key是否为null,Hashtable不允许key为null
if (key == null)
throw new NullPointerException();
// 计算哈希值
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
// 遍历链表寻找合适位置
for (Entry<K,V> e = (Entry<K,V>)tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
// 插入新元素
addEntry(hash, key, value, index);
return null;
}
- 当一个线程调用这些
synchronized
方法时,它会自动获取对象的锁。这意味着在同一时刻,只有一个线程能够执行这些方法,从而避免了多线程同时操作导致的数据不一致问题。例如,当一个线程在执行 put
方法向 Hashtable
中插入元素时,其他线程不能同时调用 put
、get
或 remove
方法,直到当前线程释放锁。
- 同步静态方法:
Hashtable
中的 contains
方法是静态同步方法。静态方法的锁是类的 Class
对象,所以当多个线程调用静态同步方法时,也会进行同步控制,保证同一时刻只有一个线程能执行该方法。例如:
public static synchronized boolean contains(Hashtable<?,?> ht, Object value) {
Entry<?,?> tab[] = ht.table;
for (int i = tab.length ; i-- > 0 ;) {
for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}
2. 锁的粒度与性能影响
- 锁的粒度:
Hashtable
采用的是对象级别的锁,即对整个 Hashtable
对象进行加锁。这意味着只要有一个线程访问 Hashtable
的任何同步方法,其他线程就无法访问该 Hashtable
的任何同步方法,锁的粒度较大。
- 性能影响:这种同步方式虽然保证了线程安全,但在高并发环境下会导致性能问题。因为多个线程可能需要等待锁的释放,从而降低了系统的并发处理能力。相比之下,
ConcurrentHashMap
采用了更细粒度的锁机制(如分段锁或 CAS
操作),可以提高高并发情况下的性能。