在一次采访中,面试官询问了并发 Hash Map 及其功能,我对此进行了详细解释。他说,在并发 HashMap 更新操作的情况下,ConcurrentHashMap 只锁定 Map 的一部分,而不是整个 Map。
所以他让我写一个简单的程序来证明,在更新操作过程中,ConcurrentHashMap 只锁定了 Map 的一部分,而不是整个 Map。我无法做到这一点,所以请告诉我如何实现这一点。
在一次采访中,面试官询问了并发 Hash Map 及其功能,我对此进行了详细解释。他说,在并发 HashMap 更新操作的情况下,ConcurrentHashMap 只锁定 Map 的一部分,而不是整个 Map。
所以他让我写一个简单的程序来证明,在更新操作过程中,ConcurrentHashMap 只锁定了 Map 的一部分,而不是整个 Map。我无法做到这一点,所以请告诉我如何实现这一点。
面试官可能期待一个简单的答案,例如:
下面的示例输出以下内容:
同步一线程:30
同步多线程:96
并发一线程:219
并发多线程:142
因此,您可以看到同步版本在高争用(16 个线程)下慢 3 倍以上,而并发版本在多线程下几乎是单线程速度的两倍。
值得注意的是,ConcurrentMap 在单线程情况下具有不可忽略的开销。
这是一个非常人为的例子,所有可能的问题都归因于微基准测试(无论如何都应该丢弃第一个结果)。但它暗示了会发生什么。
public class Test1 {
static final int SIZE = 1000000;
static final int THREADS = 16;
static final ExecutorService executor = Executors.newFixedThreadPool(THREADS);
public static void main(String[] args) throws Exception{
for (int i = 0; i < 10; i++) {
System.out.println("Concurrent one thread");
addSingleThread(new ConcurrentHashMap<Integer, Integer> ());
System.out.println("Concurrent multiple threads");
addMultipleThreads(new ConcurrentHashMap<Integer, Integer> ());
System.out.println("Synchronized one thread");
addSingleThread(Collections.synchronizedMap(new HashMap<Integer, Integer> ()));
System.out.println("Synchronized multiple threads");
addMultipleThreads(Collections.synchronizedMap(new HashMap<Integer, Integer> ()));
}
executor.shutdown();
}
private static void addSingleThread(Map<Integer, Integer> map) {
long start = System.nanoTime();
for (int i = 0; i < SIZE; i++) {
map.put(i, i);
}
System.out.println(map.size()); //use the result
long end = System.nanoTime();
System.out.println("time with single thread: " + (end - start) / 1000000);
}
private static void addMultipleThreads(final Map<Integer, Integer> map) throws Exception {
List<Runnable> runnables = new ArrayList<> ();
for (int i = 0; i < THREADS; i++) {
final int start = i;
runnables.add(new Runnable() {
@Override
public void run() {
//Trying to have one runnable by bucket
for (int j = start; j < SIZE; j += THREADS) {
map.put(j, j);
}
}
});
}
List<Future> futures = new ArrayList<> ();
long start = System.nanoTime();
for (Runnable r : runnables) {
futures.add(executor.submit(r));
}
for (Future f : futures) {
f.get();
}
System.out.println(map.size()); //use the result
long end = System.nanoTime();
System.out.println("time with multiple threads: " + (end - start) / 1000000);
}
}