前言

前段时间笔者写过一篇关于, 关于《反骨之Java是如何解决并发中的原子性问题》的博文。

其中,提出一个观点:Java中使用互斥锁和CAS解决了并发中的原子性问题。

那么,本篇博文则主要探讨的是:

Java中如何利用Java内存模型规范中的Volatile、synchronized、final关键字解决可见性问题。

(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)

正文

在开始重点之前,我们注意到上文提到的两个关键词,即内存模型可见性问题。

由于笔者在《反骨之硬件&软件为Java并发编程中挖的坑(可见性&原子性&有序性)》一文中,对可见性仅是一笔带过,所在笔者认为在本篇博文中,需要对可见性进行更进一步的解释。

所以,在谈解决可见性问题之前,我们需要聊聊可见性问题&Java内存模型。

什么是可见性问题呢?

  • 可见性,主要强调一个线程对某个共享变量进行更新之后,后续访问该共享变量的线程可能无法立即读取到更新后的最新结果,甚至永远也无法得知其他线程对该共享变量进行过修改操作。那么,这种问题,我们可以称其为:可见性问题。

当然,可见性问题是也是线程安全性的表现形式之一。

线程安全性的表现形式,即为:原子、有序、可见性。

什么是Java内存模型(JMM)?

  • 简单来说,Java内存模型是一组规范,这些规范告诉JVM如何解决原子性、有序性和可见性问题。

我们知道Java的对象一般是放在堆内存中的,而堆内存是线程共享的,所以Java内存模型的影响范围一般只涉及堆内存。这里,JMM有一张比较形象的图1-1:

我们可以看到,JMM处于线程和主内存中间,充当中介人的角色。

当线程和主内存只能通过JMM通信的时候,那么JMM就是唯一的主宰!

接下来是JMM的自我介绍:

大家好,我叫Java memory model(JMM)
为了更好的解决并发编程中的线程安全性问题,即保证并发中的原子性、有序性和可见性。
所以,我规定了以下规则。
规则一:每个线程都有独立的工作内存, 即工作内存(本地线程)
规则二:所有的共享变量都必须在主内存中, 且只能通过JMM进行控制访问。
规则三:所有的共享变量都必须在主内存中,每个线程都有自己的工作内存(本地内存),线程的所有操作都必须在工作内存中进行,而不能直接对主内存进行操作。
规则四:工作内存之间禁止互相访问。

掌握绝对权力之后,那么JMM就可以制定可见性规范:

比如:当线程A想跟线程B通信的时候

  • 首先,线程A需要把自己本地内存中更新后的共享变量副本,刷新到主内存中。
  • 随后,线程B跳过读取本地内存,直接向主内存中读取共享变量的值,将主内存中读取的值放入自己的本地内存中。

从这里可以看出,JMM是通过控制主内存和每个线程的本地内存之间的交互,来达到可见性目的的。

众所周知,JMM是一组抽象的复杂的规范,那么如何把抽象的、复杂的规范变成现实可用的方法呢?

所以,Java到底是利用什么手段或措施,保证多线程环境下,共享变量是立即可见性的(锁&volatile&final)。

  • 手段一:synchronized或Lock——互斥锁

在并发编程中,一旦使用互斥锁,一般能解决所有并发问题!

所以,可见性可以使用互斥锁进行保障。

  • 手段二:volatile关键字

在这里笔者并不讲volitale的底层实现原理,具体的底层实现细节笔者会单独写一篇博文来进行介绍。

其实,被volitale关键字修饰的共享变量,具有两个语义(这里的语义,可以理解为潜规则)。

  • 语义一:保证可见性。即,保证线程对共享变量的修改,对其他线程是立即可见的。

volatile关键字保证立即可见,其中有几点需要注意:

其一,使用volatile关键字会强制将修改的值(共享变量)立即写/读入主内存。

如何实现强制写入主内存?

在图1-1中我们可以看到存在线程A和线程B。那么,当线程A对共享变量进行修改的时候,会导致线程B工作内存中的共享变量副本失效。

如何实现强制读取主内存?

一旦线程工作内存中的共享变量副本失效,那么就必须重新从主内存中读取最新的值。

当然,看到这里相信读者们已经看出来了,volitale的语义一是利用缓存一致性协议(MESI)来保证的。

  • 语义二:保证有序性。

额…好像保证有序性在本篇博文,出现地有点不合时宜。

反正,读者们只需要记住:

volitale关键字利用禁止指令重排序和禁止编译优化,保证有序性。

  • 手段三:final关键字

有final修饰的变量(基本类型)具有不可变性,当且仅有一次赋值,一旦赋值即不可变。

不可变的变量或对象,我们可以称其为线程安全变量/对象。

因为,final关键字修饰的变量是不可变的,在多线程环境中不管怎么操作,都是同一个值。

所以,final关键字是保证可见性的手段之一。

总结

  • Java内存模型也称为内存一致性模型,是一些复杂规范的抽象集合,其中规定了工作内存和主内存的概念。

  • volitale关键字具有两种语义:保证可见性&有序性。

  • final关键字意味着不可变(基本数据类型byte, int……),所以在多线程环境下是立即可见的。

  • synchronized、volitale、final三个关键字,可以看做是Java内存模型对可见性问题提出的解决方案。

反骨之Java是如何解决并发中的可见性问题的相关推荐

  1. 【JAVA并发】二、JAVA是如何解决并发问题的

    上文提到了并发产生的原因,那么JAVA给出的解决方案是什么呢,我们来一起探讨一下. 一.解决缓存导致的并发问题 针对于这个问题,原因主要是各个缓存之间的数据可见性的问题.那么解决这个问题的最简单粗暴的 ...

  2. eclipse 新建java无scr_解决eclipse中没有js代码提示的问题

    自学js,发现eclipse中不管js文件.html文件.jsp文件没有都没js代码的提示,对于js代码也不报错,有时候就因为单词敲错却查了很久没查出来,很烦很难受. 在网上找了很多方法,都没有解决, ...

  3. java 对象逃逸 解决_Java中的逃逸问题心得

    大家一般认为new出来的对象都是被分配在堆上,但这并不是完全正确,通过对Java对象分配过程分析,我们发现对象除了可以被分配在堆上,还可以在栈或TLAB中分配空间.而栈上分配对象的技术基础是逃逸分析和 ...

  4. tomcat java 参数乱码_【java】怎样解决tomcat中get提交中文参数为乱码的问题

    详解: http://www.360doc.com/content/10/0815/14/2736180_46209475.shtml 老是碰到中文问题,再解决一小点. 这次碰到的问题是,浏览器把ur ...

  5. linux java乱码怎么解决,linux中显示中文乱码如何解决

    #第一步-排查 #第1个里程碑-看看linux系统的字符集 echo $LANG #第2个里程碑-远程连接工具 xshell/SecureCRT/putty 字符集 #第二步-修改 修复 修改字符集 ...

  6. cas无法使用_【漫画】CAS原理分析!无锁原子类也能解决并发问题!

    本文来源于微信公众号[胖滚猪学编程].转载请注明出处 在漫画并发编程系统博文中,我们讲了N篇关于锁的知识,确实,锁是解决并发问题的万能钥匙,可是并发问题只有锁能解决吗?今天要出场一个大BOSS:CAS ...

  7. 重点知识学习(8.2)--[JMM(Java内存模型),并发编程的可见性\原子性\有序性,volatile 关键字,保持原子性,CAS思想]

    文章目录 1.JMM(Java Memory Model) 2.并发编程的可见性 3.并发编程的有序性 4.并发编程的原子性 5.volatile 关键字 6.保持原子性: 加锁,JUC原子类 加锁 ...

  8. Java中有哪些无锁技术来解决并发问题?如何使用?

    除了使用 synchronized.Lock 加锁之外,Java 中还有很多不需要加锁就可以解决并发问题的工具类 一.原子工具类 JDK 1.8 中,java.util.concurrent.atom ...

  9. Java-多线程-Future、FutureTask、CompletionService、CompletableFuture解决多线程并发中归集问题的效率对比

    转载声明 本文大量内容系转载自以下文章,有删改,并参考其他文档资料加入了一些内容: [小家Java]Future.FutureTask.CompletionService.CompletableFut ...

最新文章

  1. Setting Up YARN High Availability
  2. python降序排列说true不存在_Python数据类型串讲(中)
  3. 后台服务系统之Dubbo协议
  4. Shaolin HDU - 4585(map模板题)
  5. java pc端软件抓包,如何通过抓包工具fiddler获取java程序的http请求
  6. docker搭建私有仓库
  7. python在文件中写入字典_python初学--文件操作、字典
  8. 鸿蒙电视应用市场,任正非:鸿蒙系统已上线,未来将被应用到手机、平板、电视系列产品上...
  9. 2017 ZSTU寒假排位赛 #5
  10. 网易云短信接口(验证码类短信||通知类短信)
  11. C# 将方形图片剪切为圆形(winForm)
  12. 波段高低点指标公式 k线高低点 大盘主图公式源码
  13. justinmind破解方法
  14. Conflicting order. Following module has been added:
  15. 第四章第九节数据资产盘点-数据资产目录分类
  16. 高仿360云盘android端的ui实现,(原创)高仿360云盘android端的UI实现
  17. https证书不安全是什么原因?
  18. 一个女大学生骂她男朋友的话,厉害,没一个脏字
  19. 多个List 如何取并集、交集
  20. nunit常用属性_通过在测试中使用nunit属性来帮助您的同事节省时间

热门文章

  1. [导入]【07欧美爆笑青春性喜剧】《魅力学堂_青男涩女》【DVD中字】【15:00】
  2. llinux安装mysql(按照命令复制安装就可以了,傻瓜式)
  3. MLAPI的升级之路
  4. Security:在 SIEM 上运用 Elastic Security
  5. ATI移动显卡全系列型号及参数一览表
  6. shell判断命令是否执行成功
  7. 秒 毫秒 微秒 纳秒 皮秒。。时间单位换算
  8. 微型计算机的计算器,微机简单计算器程序设计
  9. openwrt怎么做ap_终于把无线AP鸡肋的系统刷成openwrt,从此山鸡变凤凰!
  10. 计算机宽带拨号配置,宽带拨号上网怎么设置路由器