提问者:小点点

当Reader和Writer线程在ConCurrentHashMap上相同的管理时的性能


在一次面试中,面试官问我ConCurrentHashMap和HashTable有什么不同。我只是想讨论面试官不相信的一点。我说在ConCurrentHashMap中,任何数量的线程都可以同时执行读取操作,而在HashTable中,一次只能执行一个线程。然后他给出了一个ConCurrentHashMap的场景,假设一个线程在一个段上写入,同时另一个线程正在读取它的值,第二个线程会被阻塞吗??我说不,但他不相信。我在javadoc中检查了一下,上面写着…

检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和删除)重叠。检索反映了最近完成的更新操作在开始时保持的结果。

它说检索操作不会阻塞,而是添加了一般这是什么意思??

为此,我做了一个程序,其中两个线程对ConCurrentHashMap和HashTable以及同步Map执行读写操作:我在同一段上执行读写操作一秒钟

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基于不同的映射对象:ConloctHashMap:8649407 Hashtable:5284068同步Map:5438039

因此,输出证明在多线程环境中,与HashTable相比,ConCurrentHashMap更快,但不能证明在写入线程写入时,读取线程没有被阻塞以进行读取。有什么方法可以确认这一点吗??


共2个答案

匿名用户

该JavaDoc注释是为该类的最早版本编写的。它提供了一些实现灵活性并允许演进。

在非常早期的版本中,size()会乐观地对段求和,但回退到锁定以读取计数器。类似地,当映射不存在并且需要检查以解决可能的编译器重新排序时,使用readValueUnderLock。例如,这个问题由Java内存模型解决,该模型同时设计用于保证编译器可以做什么和不可以做什么。

在Java8中,哈希表从粗段重新设计为细粒度的箱。然而,computeIfAbsend总是锁定以读取或计算值,这是悲观的。在Java9中,通过避免当条目位于箱头时加锁,但如果没有,则加锁进行扫描,从而改进了这一点。在缓存之类的情况下,这可能不够好,因此Caffeine总是在计算之前执行乐观的get。在这些情况下,这可以显着提高性能。

这意味着检索可能会发生锁定,但随着设计的发展可能不会避免它。模糊的措辞允许在实现更改时保留类契约。

匿名用户

据我所知,我可能错了,我会说你是对的。get操作没有获得任何锁,但它保持了更新的发生顺序。这意味着在get之前发生的任何更新都应该反映在该检索中。另一方面,put操作确实在每个bin的基础上获得了锁,其中bin是通过散列对象值获得的。所以对hashmap的写入/更新需要锁定。正如javadocs中指出的,尽管线程同时访问同一个bin的概率很低。

相关问题