一、很多初学者分不清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. Cocos Creator一步一步实现重力球游戏

    『 游戏玩法 』 通过手机陀螺仪,调整手机,让球从上一层的间隔中落到下一层,楼层会不断上涨,如果球碰到上方或者下方的火焰,游戏结束. 『 游戏预览 』 ​ 『 开发工具 』 1. CocosCreat ...

  2. ORM查询总结版

    目录 概要 ORM常用字段 ORM基础 自定义一个插入类型,即固定长度 创建类终极版 多对多关系表创建 常用几个代码 参数 ORM与数据库代码对应的关系 外键使用分表很麻烦,要先删除主表后,再删除 不 ...

  3. 攻防世界(XCTF)WEB(进阶区)write up(四)

    ics-07  Web_php_include  Zhuanxv Web_python_template_injection ics-07 题前半部分是php弱类型 这段说当传入的id值浮点值不能为1 ...

  4. Web渗透之mssql2005 差异备份getshell

    这里记录下mssql2005差异备份拿shell的过程 http://192.168.5.21:81/index.asp?id=1;alter/**/database/**/[asp_test]/** ...

  5. Redis分布式篇

    Redis分布式篇 1 为什么 需要 Redis 集群 1.1 为什么需要集群? 1.1.1 性能 ​ Redis 本身的 QPS 已经很高了,但是如果在一些并发量非常高的情况下,性能还是会受到影响. ...

  6. Cocos2d-x 学习笔记(11.3) JumpBy JumpTo

    1. JumpBy JumpTo JumpBy,边跳边平移,不只做垂直向上的抛物动作,同时还在向终点平移.JumpTo是JumpBy的子类. 1.1 成员变量 create方法 JumpBy: Vec ...

  7. shell传递参数(三)

    $n:n代表一个数字,指执行脚本的第n个参数.特别地,$0指执行的文件名 [root@ipha-dev71- exercise_shell]# cat test.sh #!/bin/bash echo ...

  8. C语言1作业004

    这个作业属于哪个课程 C语言作业004 我在这个课程的目标是 理解和掌握for语句的基本操作 这个作业在哪个具体方面帮助我实现目标 循环结构的应用,数学函数基本问题 参考文献 C语言程序设计(第3版) ...

  9. Hadoop 在 windows 7 64位的配置(二)|非cygwin

    第一次使用需要 hdfs namenode -format 一键启动和关闭hadoop 新建文本文档 然后改名 start-hadoop.cmd 里面的内容 @echo off cd /d %HADO ...

  10. Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导

    Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导 Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导 必要的message类实现 从下面开始是在veins/src/vei ...