关于 ConcurrentHashMap,compute() 方法的一些说明
在 Java 文档中,ConcurrentHashMap 类的 compute() 方法存在一个含糊不清的地方。
在 Java 文档中,ConcurrentHashMap 类的 compute()
方法存在一个含糊不清的地方
其他线程的一些尝试更新操作可能会在计算进行时被阻塞,因此计算应该简短而简单,并且不得尝试更新此 Map 的任何其他映射。
不清楚被阻塞的是什么。是整个 Map 的访问,还是对 key 的访问? 让我们通过以下代码进行一些实验来弄清楚
package tests.concurrency;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class TestConcurrentHashMapCompute implements Runnable {
private static final Map<key, String> MAP = new ConcurrentHashMap<>();
private static final int MAX_RUNS = 100;
private static final long SLEEP_TIME = 5000;
public static void main(String[] args) {
Thread th1 = new Thread(new TestConcurrentHashMapCompute());
Thread th2 = new Thread(new TestConcurrentHashMapCompute());
th1.start();
th2.start();
}
@Override
public void run() {
final String name = Thread.currentThread().getName();
for (int i = 0; i < MAX_RUNS; i++) {
final String value = MAP.compute(new Key(name), (k, v) -> {
System.out.println(name + ": enters");
sleep();
System.out.println(name + ": exits");
return name;
});
System.out.println(name + ": " + value);
}
}
private static void sleep() {
try {
Thread.sleep(SLEEP_TIME);
} catch (Exception ex) {}
}
private static class Key {
private final String value;
private Key(String value) {
this.value = value;
}
@Override
public int hashCode() {
return 1;
// return Objects.hash(value);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Key other = (Key) o;
return Objects.equals(value, other.value);
}
}
}
这没什么复杂的,两个线程调用共享 ConcurrentHashMap
实例的 compute()
方法,使用 Key
类实例作为 Map 的 key。 但是,请注意 Key
类中的 hashCode()
方法
@Override
public int hashCode() {
return 1;
// return Objects.hash(value);
}
根据返回的内容(注释/取消注释相关行),您将看到不同的行为。 顺便说一句,根据 Java 文档,return 1
是一种有效的 hashCode()
实现
- 如果两个对象根据
equals()
方法相等,则在每个对象上调用 hashCode 方法必须产生相同的整数结果。 - 不要求如果两个对象根据
equals()
方法不相等,则在每个对象上调用hashCode
方法必须产生不同的整数结果。 但是,程序员应该意识到,为不相等的对象产生不同的整数结果可以提高哈希表性能。
结果是:
- 使用
return Objects.hash(value)
,即String
的默认hashCode()
实现,线程并行运行,就像阻塞在Key
上一样 - 使用
return 1
,线程按顺序运行,就像阻塞在整个Map
上一样。
实际上,阻塞的是由 Key
索引的 bucket。 这就是 hashCode()
方法的作用,为了快速访问相关的 bucket。 例如,HashMap
使用单链表 作为 bucket 的存储。