前言
volatile可以确保数据及时刷新到主存,但是对于读改写场景还是无能为力
举个例子
public class ConcurrentHashMapExample {public static void main(String[] args) throws InterruptedException {Map<String, Long> ordersMap = new ConcurrentHashMap<>();ordersMap.put("Delhi", 0l);ordersMap.put("London", 0l);ordersMap.put("New York", 0l);ordersMap.put("Sydney", 0l);ExecutorService service = Executors.newFixedThreadPool(2);service.submit(() -> processOrders(ordersMap));service.submit(() -> processOrders(ordersMap));service.awaitTermination(3, TimeUnit.SECONDS);service.shutdown();System.out.println(ordersMap);}private static void processOrders(Map<String, Long> ordersMap) {for (String city : ordersMap.keySet()) {for (int i = 0; i < 50; i++) {Long oldOrder = ordersMap.get(city);ordersMap.put(city, oldOrder + 1);}}}
}
复制代码
正确输出应该是
{Delhi=100, New York=100, London=100, Sydney=100}
复制代码
但是试着多运行几遍,会就发现如下的情况
{Delhi=51, New York=73, London=71, Sydney=100}
复制代码
在ConcurrentHashMap中value是用volatile修饰的,为什么还会出现这个情况呢?
static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;volatile V val;volatile Node<K,V> next;
}
复制代码
探究原因
对于读改写操作,volatile并不能保证正确,需要使用原子类解决
以volatile的自增为例
使用原子类
public class ConcurrentHashMapExample {public static void main(String[] args) throws InterruptedException {Map<String, AtomicLong> ordersMap = new ConcurrentHashMap<>();ordersMap.put("Delhi", new AtomicLong(0L));ordersMap.put("London", new AtomicLong(0L));ordersMap.put("New York", new AtomicLong(0L));ordersMap.put("Sydney", new AtomicLong(0L));ExecutorService service = Executors.newFixedThreadPool(2);service.submit(() -> processOrders(ordersMap));service.submit(() -> processOrders(ordersMap));service.awaitTermination(1, TimeUnit.SECONDS);service.shutdown();System.out.println(ordersMap);}private static void processOrders(Map<String, AtomicLong> ordersMap) {for (String city : ordersMap.keySet()) {for (int i = 0; i < 50; i++) {ordersMap.get(city).incrementAndGet();}}}
}
复制代码
主要参考这篇文章