当读写器线程位于ConcurrentHashMap上时的java性能
在一次采访中,面试官问我ConcurrentHashMap与HashTable有何不同。我只想讨论一下面试官不相信的一点。我说过,在ConcurrentHashMap中,任意数量的线程都可以同时执行读取操作,而在HashTable中,一次只能执行一个线程。然后他给出了ConcurrentHashMap的一个场景,假设一个线程在一个段上写,同时另一个线程正在读取它的值,那么第二个线程会被阻塞吗??我说不,但他不相信。我查了javadoc,上面写着
检索操作(包括get)通常不会 块,因此可能与更新操作(包括put)重叠 然后移除)。检索结果反映了大多数 最近完成的更新操作保留了 起病
它说检索操作不会阻止,而是添加了generally
这是什么意思
为此,我制作了一个程序,其中两个线程对ConcurrentHashMap和HashTable以及synchronizedMap执行读写操作: 我在同一段上执行读写操作一秒钟
class ReaderThread extends Thread {
private Map<String, Integer> map;
private static boolean b = false;
public ReaderThread(Map<String, Integer> map) {
this.map = map;
}
public void run() {
long startTime = System.nanoTime();
long endTime = 0;
while (!b) {
map.get("A");
endTime = System.nanoTime();
if (endTime - startTime > 1000000000L)
b = true;
}
}
}
class WriterThread extends Thread {
private Map<String, Integer> map;
private static int n = 0;
private static boolean b = false;
public WriterThread(Map<String, Integer> map) {
this.map = map;
}
public void run() {
long startTime = System.nanoTime();
long endTime = 0;
while (!b) {
map.put("A", n++);
endTime = System.nanoTime();
if (endTime - startTime > 1000000000L)
b = true;
}
}
}
public class DiffrentMapReadWritePerformanceTest {
public static void main(String[] args) throws InterruptedException {
Map<String, Integer> map = new ConcurrentHashMap<>();
// Map<String, Integer> map = new Hashtable<>();
// Map<String, Integer> map = new HashMap<>();
// map = Collections.synchronizedMap(map);
Thread readerThread = new ReaderThread(map);
Thread writerThread = new WriterThread(map);
writerThread.start();
readerThread.start();
writerThread.join();
readerThread.join();
System.out.println(map.get("A"));
}
}
以及基于不同地图对象的O/p: ConcurrentHashMap:8649407 哈希表:5284068 同步地图:5438039
因此,输出证明ConcurrentHashMap在多线程环境中相对于HashTable是快速的,但不能证明在编写器线程写入时,读线程没有被阻塞以进行读取。有没有办法证实这一点
# 1 楼答案
JavaDoc注释是为类的最早版本编写的。它提供了一些实现灵活性,并允许进行改进
在非常早期的版本中,size()会乐观地对段进行求和,但会退回到锁定来读取计数器。类似地,当缺少映射并且需要进行检查以解决可能的编译器重新排序时,也会使用readValueUnderLock。例如,Java内存模型就解决了这个问题,它同时被设计来保证编译器能做什么和不能做什么
在Java8中,哈希表从粗段重新设计为细粒度的容器。然而
computeIfAbsent
总是锁定读取或计算值,因此非常悲观。在Java9中,这是通过在条目位于垃圾箱的头部时避免锁定来实现的,但如果不是,则锁定扫描。在缓存之类的情况下,这可能不够好,因此咖啡因在计算之前总是执行乐观的get
。在这些情况下,这可能会提高性能这意味着检索时可能会发生锁定,但随着设计的发展,可能不会发生锁定以避免它。模糊的措辞允许类契约在实现更改时保留