线程从concurrentMap读取到指定key的值时不向集合写数据,下面的代码存在多个线程写数据

废话不多讲,直接贴代码:

 @Test
    public void test02() {
        try {
            log.debug("开始测试");
            ConcurrentMap<String, String> map = CacheBuilder.newBuilder()
                    .initialCapacity(10)
                    .expireAfterAccess(1, TimeUnit.HOURS)
                    .<String, String>build()
                    .asMap();

            CountDownLatch testMainCountDownLatch = new CountDownLatch(1);

            int count = 100;
            ExecutorService executorService = Executors.newFixedThreadPool(count);
            CountDownLatch countDownLatch = new CountDownLatch(count);
            for (int i = 0; i < count; i++) {
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            log.debug("{}线程开始了", Thread.currentThread().getName());
                            Thread.sleep(1000);
                            countDownLatch.await();
                            if (StringUtils.isBlank(map.get("hello"))) {
                                log.debug("{}线程没有读取到值,开始写值", Thread.currentThread().getName());
                                map.put("hello", Thread.currentThread().getName());
                            } else {
                                log.debug("{}线程读取到值:{}", Thread.currentThread().getName(), map.get("hello"));
                            }
                        } catch (InterruptedException e) {
                            log.error(ExceptionUtils.getStackTrace(e));
                        }
                    }
                });
                countDownLatch.countDown();
            }


            testMainCountDownLatch.await();
        } catch (InterruptedException e) {
            log.error(ExceptionUtils.getStackTrace(e));
        }

    }

输出存在多个线程向concurrentMap中写值。
我理解应该只有一个线程向concurrentmap中写值。

当多个线程运行到下面代码的第一行时,首先由于ConcurrentHashMap的get方法实际上未加锁,if判断的结果在大多数线程中为真,于是if中语句被执行。
而当多个线程运行到第二行时,此时更不存在加锁,结果同上。
直到有线程运行到第3行时,map才会加锁,但此时已经为时已晚。

if (StringUtils.isBlank(map.get("hello"))) {
  log.debug("{}线程没有读取到值,开始写值", Thread.currentThread().getName());
  map.put("hello", Thread.currentThread().getName());
}

以下是java11中ConcurrentHashMap的get方法源码

    public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
                (e = tabAt(tab, (n - 1) & h)) != null) {
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
                if (e.hash == h &&
                        ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }