前提:什么是Volatile? Java 虚拟机提供轻量级的同步机制

  1、保证可见性------->JMM
  2、不保证原子性
  3、禁止指令重排

一、什么是JMM

  1.JMM : Java内存模型,不存在的东西,一个概念或者约定

  2.关于JMM的一些同步的约定:

    1、线程解锁前,必须把共享变量立刻刷回主存

    2、线程加锁前,必须读取主存中的最新值到工作内存中!

    3、加锁和解锁是同一把锁

  3.8种操作:

        

  内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

    lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态    

    unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

    read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用

    load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中

    use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令

    assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中

    store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用

    write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

JMM对这八种指令的使用,制定了如下规则:

    不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write

    不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存

    不允许一个线程将没有assign的数据从工作内存同步回主内存

    一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store操作之前,必须经过assign和load操作

    一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁

    如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值

    如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量

    对一个变量进行unlock操作之前,必须把此变量同步回主内存

二、验证Volatile的三特性

  1.验证可见性

public class Demo {
/**
* 不加 volatile 程序就会死循环!
* 加 volatile 可以保证可见性
*/
private volatile static int num = 0; public static void main(String[] args) { // main
new Thread(() -> { // 线程 1 对主内存的变化不知道的
while (num == 0) {
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}

2.验证不保证原子性

    原子性 : 不可分割

    线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败

public class Demo {
// volatile 不保证原子性
private volatile static int num = 0; public static void add() {
num++;
} public static void main(String[] args) {
//理论上num结果应该为 2 万
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) { // main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " " + num);
}
}

       拓展:不使用lock跟synchronize怎样保证原子性?使用原子类,解决 原子性问题

public class Demo {

    // 原子类的 Integer
private volatile static AtomicInteger num = new AtomicInteger(); public static void add() {
// num++; // 不是一个原子性操作
num.getAndIncrement(); // AtomicInteger + 1 方法, CAS
} public static void main(String[] args) {
//理论上num结果应该为 2 万
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) { // main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " " + num);
}
}

    3.禁止指令重排

      什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的。

      源代码-->编译器优化的重排--> 指令并行也可能会重排--> 内存系统也会重排---> 执行处理器在进行指令重排的时候,考虑:数据之间的依赖性!   

        线程 1               线程 2
         x2 = a ;             x1 = b ;
         b = 1;                a = 2 ;
     两个线程同时执行,分别有1、2、3、4四段执行代码,其中1、2属于线程1 , 3、4属于线程2 ,从程序的执行顺序上看,似乎不太可能出现x1 = 1 和x2 = 2 的情况,但实际上这种情况是有可能发现的,因为如果编译器对这段程序代码执行重排优化后,可能出现下列情况

        线程 1             线程 2
         b = 1;             a = 2 ;
        x2 = a ;           x1 = b ;

      为什么能禁止指令重排呢?内存屏障(在单例模式下用的最多),CPU指令

        

JUC---10JMM的更多相关文章

  1. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  2. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

  3. 【目录】JUC锁框架目录

    JUC锁框架的目录整理如下: 1. [JUC]JUC锁框架综述 2. [JUC]JDK1.8源码分析之LockSupport(一) 3. [JUC]JDK1.8源码分析之AbstractQueuedS ...

  4. 【目录】JUC集合框架目录

    JUC集合框架的目录整理如下: 1. [JUC]JUC集合框架综述 2. [JUC]JDK1.8源码分析之ConcurrentHashMap(一) 3. [JUC]JDK1.8源码分析之Concurr ...

  5. java多线程系类:JUC原子类:01之框架

    本系列内容全部来自于http://www.cnblogs.com/skywang12345/p/3514589.html 特在此说明!!!!! 根据修改的数据类型,可以将JUC包中的原子操作类可以分为 ...

  6. Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例

    概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...

  7. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  8. Java多线程系列--“JUC锁”01之 框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...

  9. 【JUC】JUC锁框架综述

    一.前言 在分析完了集合框架后,很有必要接着分析java并发包下面的源码,JUC(java.util.concurrent)源码也是我们学习Java迈进一步的重要过程.我们分为几个模块进行分析,首先是 ...

  10. 【JUC】JUC集合框架综述

    一.前言 完成了JUC的锁框架的分析后,现在分析JUC集合框架,之前分析过的集合框架,很大程度上都不是线程安全的,其在多线程环境下会出现很多问题,为了保证在多线程环境下仍然能够正确安全的访问集合,出现 ...

随机推荐

  1. dubbo学习(五)注册中心zookeeper

    初识zookeeper 下载地址:https://archive.apache.org/dist/zookeeper/ 详细的ZooKeeper教程戳这里~ PS: 建议目前选择3.4的稳定版本进行使 ...

  2. pwnable.kr-lotto-witeup

    执行分析题目代码,发现是输入值和十进制是1到45的ASCII码系统生成对应字母做比较:而比较方法是遍历输入值的所有位和系统生成字符串的每个位作比较,相同计数为6则爆出flag.漏洞啊,只要押中有一字母 ...

  3. Hbuilder获取手机当前地理位置的天气

    前言:前面一段时间,公司项目里有一个需求 是获取当前手机地理位置当天的天气情况  将实时天气信息提供给客户.在网上搜索资料时候,发现知识很零碎,自己实现以后整理出来,方便于各位的学习与使用. 一.获取 ...

  4. [Vue warn]: Error in render: "TypeError: Cannot read property 'matched' of undefined" found in <App> at src/App.vue

    当用Vue模块化开发时,输入  http://localhost:8080  页面没有显示,首先按F12,检查是否有如下错误 话不多说,直接看下面: 解决方法1 如果是上面出的问题,以后就要注意了哦, ...

  5. 047 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 09 嵌套while循环应用

    047 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 09 嵌套while循环应用 本文知识点:嵌套while循环应用 什么是循环嵌套? 什么是循环嵌套? ...

  6. Java知识系统回顾整理01基础04操作符05赋值操作符

    一.赋值操作 赋值操作的操作顺序是从右到左 int i = 5+5; 首先进行5+5的运算,得到结果10,然后把10这个值,赋给i public class HelloWorld { public s ...

  7. Splay浅谈

    Splay是众多平衡树之一,它的功能十分强大,但常数极大.在LCT和许多数据结构中都能用到. Splay的核心操作,就是rotate.为了使树不是一条链,而是平衡的,我们需要旋转来维护形态.理论很简单 ...

  8. springCloud项目搭建

    新建父maven项目 groupId:pers.xzp.springCloudartifactId:springCloud 父项目中仅仅需要一个pom文件,用于管理模块的依赖统一.继承等 编辑pom文 ...

  9. CN1,CN2 GT和CN2 GIA的区别

    用一句话来概括,CN1主要定位于承载普通质量的互联网业务,而CN2则定位于承载企业VPN业务.中国电信的自营业务及高质量的互联网业务,CN2 GIA又比GT要好一些. 顺序:CN2 GIA>CN ...

  10. RocketMQ的消息是怎么丢失的

    前言 通过之前文章的阅读,有关RocketMQ的底层原理相信小伙伴们已经有了一个比较清晰的认识. 那么接下来王子想跟大家讨论一个话题,如果我们的项目中引入了MQ,势必要面对的一个问题,就是消息丢失问题 ...