一、很多初学者分不清JMM和JVM的内存模型,本篇只是简要的谈一谈什么是JMM,并不深入探讨。

  示意图A:

  

  在多线程操纵共享资源时,并不是对资源本身进行的操作,而是将共享资源的副本复制了一份到自己的私有空间中,等使用完了再写回去覆盖原资源,我可能在瞎说,你先别信,举个例子来验证一下:

  


class Number{
int count = 0;
public void add(){
this.count = 1;
}
}
public class Demo2 {
public static void main(String[] args) {
Number number = new Number(); new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"***come in");
try{
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
number.add();
System.out.println(Thread.currentThread().getName()+" newCount: " + number.count);
},"A").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"***come in");
while (number.count == 0){
try{
Thread.sleep(1000);
System.out.println("wait...");
            //重新从主内存获取资源(number.count)
            //System.out.println(Thread.currentThread().getName() + " newCount: " + number.count);
                }catch (Exception e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" newCount: " + number.count);
},"B").start();
}
}
 

  输出结果很奇怪:A线程将count设置为1结束后,B线程却不停的每秒钟输出一个wait... , 为什么A线程将count设置为1,B没有跳出while循环呢?因为B并不知道A已经将count值改变了,A线程用的count是之前从主内存拷贝而来的,而不是直接使用的主内存中的number的count。A也是先将主内存中的内容复制到自己的私有工作内存空间中,但是进行操作后便将数据写会到主内存中,但这些操作B线程并不知道,只有B线程重新获取资源才会知道主内存资源发生了改变,因此如何让B线程“知道”A线程已经将数据改变,是由现实需求的。

二、volatile关键字

class Number{
volatile int count = 0;
public void add(){
this.count = 1;
}
}

  给count变量加一个volatile关键字修饰,A线程修改count值后,即使B线程没有主动从主内存中获取最新资源,也会有一种“通知机制”告诉他你的值不是最新的了,你需要从主内存中获取最新的资源,两个线程是这样,多个线程也是这样。我们称这样的通知机制为“可见性”。

  可见性:1、修改volatile变量时会强制将修改后的值刷新到主内存中

      2、修改volatile变量后会导致其他线程工作内存中的对应变量值失效。因此,再读取该变量值的时候就需要重新从主内存中读取新值。

  

  上面的add()方法里面操作时原子性操作,如果如果把add()方法改成:

class Number{
volatile int count = 0;
public void add(){
this.count = count++;//count++不是原子性操作
}
}

  输出结果和没有加volatile是一样的,还是不停的输出wait... ,也就是说volatile适用于原子性操作,那如果对变量的操作不是原子性操作,怎么才能实现这种通信呢?使用锁!

class Number{
volatile int count = 0;
public synchronized void add(){
this.count = count++;
}
}
class Number{
Lock lock = new ReentrantLock();
volatile int count = 0;
public synchronized void add(){
lock.lock();
try{
this.count = count++;
}finally {
lock.unlock();
}
}
}

  通过加锁,也可以实现 “原子性”。我们来分析一下为什么为什么count++不是原子性操作,执行count++,一共分为三步,将内存中的count读到CPU寄存器,然后执行自增操作,最后写回内存。

简述JMM的更多相关文章

  1. 简述 OAuth 2.0 的运作流程

    本文将以用户使用 github 登录网站留言为例,简述 OAuth 2.0 的运作流程. 假如我有一个网站,你是我网站上的访客,看了文章想留言表示「朕已阅」,留言时发现有这个网站的帐号才能够留言,此时 ...

  2. JavaScript单线程和浏览器事件循环简述

    JavaScript单线程 在上篇博客<Promise的前世今生和妙用技巧>的开篇中,我们曾简述了JavaScript的单线程机制和浏览器的事件模型.应很多网友的回复,在这篇文章中将继续展 ...

  3. Design Patterns Simplified - Part 3 (Simple Factory)【设计模式简述--第三部分(简单工厂)】

    原文链接:http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part3-factory/ Design ...

  4. 浅析java内存模型--JMM(Java Memory Model)

    在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...

  5. JMM(java内存模型)

    What is a memory model, anyway? In multiprocessorsystems, processors generally have one or more laye ...

  6. Android网络定位服务定制简述

    Android 添加高德或百度网络定位服务 Android的网络定位服务以第三方的APK方式提供服务,由于在国内Android原生自带的com.google.android.gms服务几乎处于不可用状 ...

  7. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

  8. JMM和happens-before原则

    JMM: Java Memory Model(Java内存模型),围绕着在并发过程中如何处理可见性.原子性.有序性这三个特性而建立的模型. 可见性: JMM提供了volatile变量定义.final. ...

  9. 简述ASP.NET MVC原理

    1.为什么ASP.NET需要MVC? 因为随着网站的的数量级越来越大,原始的网站方式,这里指的是WebForm,在运行速度和维护性方面,以及代码量上面,越来越难以满足日益庞大的网站维护成本.代码的重构 ...

随机推荐

  1. ssrf漏洞学习(PHP)

    自己最近原本是想深入的学习一下关于xss.csrf的东西的,可是感觉这些东西需要有很好的js的基础来进行学习..还有感觉自己感觉也差不多该要学习内网渗透了..正好ssrf在内网这一块也是比较有用的.于 ...

  2. Python 爬虫从入门到进阶之路(十)

    之前的文章我们介绍了一下 Python 中正则表达式和 re 模块来做一个案例,爬取<糗事百科>的糗事并存储到本地.本章我们来看一下另一种爬取数据的方式 XPath. 我们在前面爬取< ...

  3. python编程基础之十三

    列表的通用操作: list1 = [43, 65, 76, 6] list2 = [45, 77, 90, 11, 2, 4, 66] print(list1+ list2)  # 列表组合 prin ...

  4. SQL SERVER数据库内,需要序列号进行排序时,排序方法

    举例如下: SELECT ROW_NUMBER()OVER(ORDER BY (SELECT 0)) AS ROWNUM,* FROM YXHIS2018..TBMZGHMX201801 --不排序S ...

  5. Idea项目注释规范设置

    Idea项目注释规范设置文档 1.类注释: /**    *@ClassName: ${NAME}    *@Description: TODO    *@Author: guohui    *@Da ...

  6. Ubuntu16.04 安装apache+mysql+php(LAMP)

    记录下ubuntu环境下安装apache+mysql+php(LAMP)环境. 0x01安装apache sudo apt-get update sudo apt-get install apache ...

  7. Hadoop和YARN :map+shuffle+reduce走读

    今天做了一个hadoop分享,总结下来,包括mapreduce,及shuffle深度讲解,还有YARN框架的详细说明等. v\:* {behavior:url(#default#VML);} o\:* ...

  8. SpringBootSecurity学习(26)前后端分离版之github单点登录

    单点登录(SSO) 关于oauth2.0,最后我们再来学习一下单点登录.前面介绍过单点登录的定义,单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一. ...

  9. 生产环境中利用软链接避免"rm -rf /"的方法

      1.将系统中的rm二进制文件重命名为rm_real:2.编写脚本rm_shell,rm_shell中主要包含以下内容:    2.1)路径转换模块,用于将rm_shell参数中的路径转换为绝对路径 ...

  10. LWIP移植文件介绍

    在介绍文件之前首先介绍一下DMA描述符 stm32以太网模块接收/发送FIFO和内存之间的以太网传输是通过以太网DMA使用DMA描述符完成的,一共有两个描述符列表:一个用于接收,一个用于发送, 两个列 ...