前言

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的自增为例

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();}}}
}
复制代码

主要参考这篇文章

java中如何应对读改写场景相关推荐

  1. Java中那些内存泄漏的场景!

    虽然Java程序员不用像C/C++程序员那样时刻关注内存的使用情况,JVM会帮我们处理好这些,但并不是说有了GC就可以高枕无忧,内存泄露相关的问题一般在测试的时候很难发现,一旦上线流量起来可能马上就是 ...

  2. java中会用到二进制吗,java中的二进制运算以使用场景

    本文知识点 Java 中用二进制使用场景 Java 中声明二进制数据 Java 中拼接二进制数据 二进制的使用场景 做标识用 二进制就是只有 0 和 1 这两个数.这和我们现实很多场景都类似, 如男/ ...

  3. 谈谈 Java 中自定义注解及使用场景

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:快给我饭吃 www.jianshu.com/p/a7bedc ...

  4. java 判断文件是否可读_如何检查文件在Java中是否可读,可写或可执行?

    通常,无论何时创建文件,都可以限制/允许某些用户读取/写入/执行文件. 在Java文件中(它们的抽象路径)由java.io包的File类表示.此类提供了各种方法来对文件执行各种操作,例如读取,写入,删 ...

  5. Java中静态变量的适用场景

    Java类中的静态变量在程序运行期间,其内存空间对所有该类的对象实例而言是共享的,有些时候可以认为是全局变量.因此在某些时候为了节省系统内存开销.共享资源,可以将类中的一些变量声明为静态变量,通过下面 ...

  6. java 字符串原子变量,如何在java中提供原子读/写2个变量?

    在我的课堂上,我的代码如下: int counter1; int counter2; public void method1(){ if (counter1>0) { ...........do ...

  7. Java中的互斥锁介绍

    前言   互斥锁是一种广泛应用于多线程编程中的并发控制机制.在Java中,互斥锁有很多不同的实现方式,在本文中我们将介绍Java中常见的几种互斥锁实现方式,并讲解它们的用法.原理和代码案例. sync ...

  8. 深入分析 Java 中的中文编码问题(键人岐)

    编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换较多.本文将向你详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中 ...

  9. Java中的内存泄露的几种可能

    转载自  Java中的内存泄露的几种可能 Java内存泄漏引起的原因: 内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏. 长生命周 ...

最新文章

  1. CodeForces 157A Game Outcome
  2. qtcreator cannot find -lts
  3. java如何写对象配置文件,Java 读写Properties配置文件详解
  4. 刚回到北京,倒时差中……
  5. Windows界面编程-背景图片、透明特效使用
  6. matlab delete、clf、cla、close、closereq删除对象
  7. 【科普】OFFICE 365 outlook 如何导入其他邮箱的联系人
  8. 为什么说容器的崛起预示着云原生时代到来?
  9. 20171221L09-10老男孩Linux运维实战培训-Nginx服务生产实战应用指南02
  10. 一次Python性能调优经历
  11. Python 数据结构与算法——插入排序(insertion sort)
  12. 求三角形面积(C++)
  13. android:launchMode=“singleTask“
  14. 英文词根词典简化笔记
  15. html-QQ登陆界面
  16. 振耀退休感言及海辉执行董事长视频访谈
  17. Android WallpaperManager 同时设置桌面壁纸与锁屏的问题
  18. Web——HTML常见标签及用法
  19. Vue将HTML内容用打印机打印出来
  20. 互联网产品需求分析思路与方法

热门文章

  1. centos 脚本基础练习1
  2. 安全的Web主机iptables防火墙脚本
  3. wps 2016 个人版 重新开始编号
  4. .NET环境下有关打印页面设置、打印机设置、打印预览对话框的实现
  5. 图像处理工具包ImagXpress的多页TIFF编辑API的使用(1)
  6. 织梦dedecms如何快速使用拼音首字母做栏目名称
  7. JSTL 读取数组 和 字符串连接
  8. PHP教程中验证正整数is_int($value+0),为什么要这样?
  9. cmder里ls、pwd、自定义的alias等一系列命令都无法使用
  10. Python 语法相关知识