65.9K
CodeProject 正在变化。 阅读更多。
Home

关于 ConcurrentHashMap,compute() 方法的一些说明

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2020 年 7 月 28 日

CPOL

2分钟阅读

viewsIcon

3670

在 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 的存储。

© . All rights reserved.