废话不多讲,直接贴代码:
@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;
}