谈谈硬件架构模型


先谈谈硬件是如何工作的,举个例子,你在window操作系统上需要下载一个游戏(20M),就需要使用cpu和内存了,在这个过程中cpu负责计算,比如计算下载进度,统计下载完成一共需要多少时间等,内存为cpu提供数据的,负责保存游戏的所有信息,比如游戏的大小(20M)数据。在这个过程中,cpu从内存上取游戏大小这个数据,然后cpu去计算下载进度,把计算出的进度结果再写到内存,最终呈现到用户页面,大概对cpu和内存应该有个大概的认识了吧!看上去下载游戏这个过程分工明确,没有问题,但实际上cpu的计算速度比内存的存取速度高了不知道多少个数量级,这个过程cpu很空闲啊(如图一),cpu你闲着没事干那就是浪费资源浪费钱啊,这是个问题,于是人们就想了个办法,在内存上面加个(高速)缓存,如果是一些常用信息,比如游戏大小这个数据,那就不用在内存取了,直接在缓存上拿(如图二),而缓存设计的存取速度是很快的,当然价格也更高,如果刚好缓存上有这个游戏大小数据,这个操作在计算机的世界叫做缓存命中,这样就解决了cpu很闲的问题。哈哈,还是举个简单例子吧,咱春节买票回家,尽管你的手速很快,但是还是一票难求,12306官网响应速度慢,没办法家还是要回的,那就找黄牛,虽然价格贵但是能解决你的痛点。这个例子中你,12306系统,黄牛分别对应cpu,内存和缓存,方便你理解。顺便说下,这个黄牛其实也是设计模式中的代理。(图三,图四)

案例分析JMM不可见性


了解了硬件架构,再来理解Java内存模型(JMM),如鱼得水,JMM是根据硬件架构映射出来的,不是真实存在的,硬件模型是物理的,是真实存在的,如下图所示,如果现在有两个线程AB需要同时将共享变量c的值加1,最终的程序运行的结果c的值可能是3,也可能是2。那我们一起来看看程序执行过程吧,程序初始化,线程AB将拷贝主内存的共享变量c到各自的工作内存,此时工作内存A,工作内存B的初始化值c值都为1,初始化结束,如下图所示。这里可以把线程A理解成cpu1,线程B理解成cpu2,工作内存理解成高速缓存。这个过程因为工作内存是线程私有的,因为每个高速缓存是属于不同CPU是不可见的,工作内存A看不见工作内存B的c值为1,相反工作内存B也看不到工作内存A的c值。

当线程AB同时将共享变量c加1时,如果线程A先获取时间片,此时工作内存A的c值加1等于2,然后由工作内存A将变量c=2同步到主内存,此时主内存c变量为2了,线程A执行结束,释放时间片给线程B,如下图所示。此时主内存会更新线程B的工作内存B,将c=2告诉线程B,并更新工作内存B的值c=2,此时B获取时间片,看到工作内存B值是c=2,加1,c=3,线程B将c=3写到主内存,此时主内存c的值就是3了,线程B执行结束,整个程序结束。其实在这个过程中,还有一种意外情况,如果线程A执行结束后,将主内存的c值变为2,如果主内存c=2还没有同步更新到工作内存B呢?此时问题就来了,线程B获取时间片后发现自己的工作内存变量c还是1,然后加1,此时c=2,将c再更新到主内存,此时主内存的值还是2,主内存再同步c=2的值给线程B已经失去意义了,因为线程全部执行完毕。在这个程序执行过程中,其实导致线程安全的本质问题是主内存通知不及时才导致发生的,这个案例中因为主内存不能及时将c=2的值更新到线程B的工作内存,导致线程B获取不到c已经更新为2了。

那问题来了,cpu各自带着私有缓存,线程带着各自私有工作内存,数据都靠着主内存来通信,但是主内存偏偏又不给力啊,通知线程B的工作内存不给力,导致结果c=2或者c=3的,这就是出现了线程安全问题了,这种安全性问题是由于缓存不可见造成的,于是我开始怀恋单核cpu时代了,但是逃避也解决不了实际问题,于是那些聪明的人们就想既然缓存不一致,那所有缓存都实现统一的协议可以吗,下面我们就简单聊下缓存一致性协议。

硬件缓存不一致方案


1)总线Lock#锁。锁定总线的开销比较大,在缓存更新内存后,其他的cpu都会被锁定住,禁止与内存通信,这样开销就大了。

2)MESI协议。这是缓存一致性协议的具体实现,它通过嗅探技术识别哪个cpu想修改主内存缓存行信息,如果该缓存行是共享的,先将该缓存行刷新到主内存,再设置其他cpu的高速缓存的缓存行无效,但频繁的嗅探其他cpu想修改的共享数据,也会导致总线风暴。

什么是可见性


可见性,有序性,原子性是线程安全的三个重要指标。可见性对理解多线程非常非常非常重由于多核硬件架构的问题,cpu高速缓存之间本身是不可见的,必须要实现缓存一致性协议。我们刚才上面也说了硬件方面的方案,多线程对共享变量是不可见的,Java方面也提供了两个关键字来保证多线程情况下共享变量的可见性方案。

volatile实现可见性


在JVM手册中,当多线程写被volatile修饰的共享变量时,有两层语义。

1)该变量立即刷新到主内存。

2)使其他线程的共享变量立即失效。言外之意当其他线程需要的时候再从主内存取。

在上述案例中,如果c为一个布尔值并且被volatile修饰,那么当线程AB同时更新共享变量c时,此时c对于工作内存AB是可见的。

synchronized实现可见性


在JVM手册中,synchronized可见性也有两层语义。

1)在线程加锁时,必须从主内存获取值。

2)在线程解锁时,必须把共享变量刷新到主内存。

这两句说明了,时刻保持主内存数据最新,当新的线程获取锁需要从主内存获取值。

总结


今天通过可见性的话题,引出了硬件架构,硬件架构因为多cpu高速缓存引出的不可见性问题,从而引出了解决可见性的方案,这是基于硬件的。从Java高级语言的角度,引出了基于硬件的映射模型JMM,并给出了jvm要实现可见性用的一些关键字。谢谢大家的观看,我是叫练,边叫边练。有疑问和错误欢迎留言和指正。

可见性是什么?(通俗易懂)相关推荐

  1. JMM中的原子性、可见性、有序性和volatile关键字

    相信如果对JMM底层有过了解或者接触过java并发编程的读者对以上的概念并不陌生,但是真正理解的可能并不多.这里我就对这些概念再做一次讲解.相信读者多读几遍应该就有自己的理解,实在不理解也没关系,说明 ...

  2. 3万字通俗易懂告诉你什么是.NET?什么是.NET Framework?什么是.NET Core?

    通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先 ...

  3. 3万字长文概述:通俗易懂告诉你什么是.NET?什么是.NET Framework?什么是.NET Core?

    [转载]通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的 ...

  4. 通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 转自:https://www.cnblogs.com/1996V/p/9037603.html#net1...

    通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先 ...

  5. 通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? .Net Web开发技术栈...

    通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先 ...

  6. java cas 什么意思_什么是CAS机制,通俗易懂大白话版。

    原标题:什么是CAS机制,通俗易懂大白话版. 我们先看一段代码: 启动两个线程,每个线程中让静态变量count循环累加100次. 最终输出的count结果一定是200吗?因为这段代码是非线程安全的,所 ...

  7. 一文让你彻底搞懂AQS(通俗易懂的AQS)

    一文让你彻底搞懂AQS(通俗易懂的AQS) 一.什么是AQS AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Se ...

  8. java中实现具有传递性吗_Java中volatile关键字详解,jvm内存模型,原子性、可见性、有序性...

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

  9. Java注解---通俗易懂

    本文转载于Java注解-最通俗易懂的注解 Annotation 中文译过来就是注解.标释的意思,在 Java 中注解是一个很重要的知识点,但经常还是有点让新手不容易理解. 我个人认为,比较糟糕的技术文 ...

最新文章

  1. 图像滤波与OpenCV中的图像平滑处理
  2. 虚拟串口工具_com0com
  3. 对CAS机制的理解(二)
  4. 问题Re-installation failed due to different application signatures.解决
  5. ZOJ4116 Game on a Graph
  6. AJAX+JavaScript无刷新检查用户名
  7. docker用gpu的参数_ZStack实践汇 | ZStack+Docker支撑GPU业务实践
  8. 【Redis】Redis 乐观锁
  9. SaltStack二次开发api
  10. 190529每日一句,放胆去梦想,努力去奋斗
  11. 【jQuery进阶】子菜单插件Slight Submenu
  12. BZOJ4567 [SCOI2016]背单词
  13. 你是哪一型---左右脑性格测试(转)
  14. python练习题-运算符和变量练习作业题
  15. 细胞穿透肽八精氨酸、 H2N-RRRRRRRR-OH、 148796-86-5
  16. [n年以前的诗] 你别这样
  17. Blah数集(信息学奥赛一本通 - T1333)
  18. IE浏览器歇菜之后,建模助手干了这件事
  19. POI:Excel计算公式的再计算
  20. 开机流程,模块管理与loader0.0

热门文章

  1. 软考中级系统集成项目管理工程师自学好不好过,怎么备考?
  2. Thymeleaf是什么
  3. 简单--油猴(tampermonkey)脚本安装过程
  4. 精确率(Precision)、召回率(Recall)与F1值
  5. OpenGL 坐标系统
  6. 雅思写作 Day 1 阅读笔记
  7. 景观手绘剖面商业空间设计作品集合
  8. PIT systick
  9. 中国与西欧的历史发展为何不同
  10. 抗投诉服务器是什么意思