前提:什么是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. JVM性能调优(2) —— 垃圾回收器和回收策略

    一.垃圾回收机制 1.为什么需要垃圾回收 Java 程序在虚拟机中运行,是会占用内存资源的,比如创建的对象.加载的类型数据等,而且内存资源都是有限的.当创建的对象不再被引用时,就需要被回收掉,释放内存 ...

  2. myisamchk是用来做什么的?MyISAM Static和MyISAM Dynamic有什么区别?

    myisamchk是用来做什么的? 它用来压缩MyISAM[歌1] 表,这减少了磁盘或内存使用. MyISAM Static和MyISAM Dynamic有什么区别? 在MyISAM Static上的 ...

  3. 一篇文章搞定 Nginx 反向代理与负载均衡

    代理 要想弄明白反向代理,首先要知道啥是正向代理,要搞懂正向代理只需要知道啥是代理即可.代理其实就是一个中介,在不同事物或同一事物内部起到居间联系作用的环节.比如买票黄牛,房屋中介等等. 在互联网中代 ...

  4. SpringBoot-03-JSR303数据校验和多环境切换

    3.3 JSR303数据校验 先看如何使用 ​ Springboot中可以用@Validated来校验数据,如果数据异常则统一抛出异常,方便异常中心统一处理. ​ 这里我们写个注解让name只支持Em ...

  5. 国产化之路-安装达梦DM8数据库

    专题目录 国产化之路-统信UOS操作系统安装 国产化之路-国产操作系统安装.net core 3.1 sdk 国产化之路-安装WEB服务器 国产化之路-安装达梦DM8数据库 国产化之路-统信UOS + ...

  6. Java知识系统回顾整理01基础02面向对象03方法

    一.根据实例给出"方法"的定义 在LOL中,一个英雄可以做很多事情,比如超神,超鬼,坑队友 能做什么在类里面就叫做方法 比如队友残血正在逃跑,你过去把路给别人挡住了,导致他被杀掉. ...

  7. 南方IT比赛项目

    注意: 出现以下提示点击否就可以了 导入工作台: 导入模型 安装 安装到工作台上 修改模型位置 更新工具位置 点击是 改角度 九十度 添加组件 改一下名字,方便记忆 把工具移到组件 拆除后将工具移到S ...

  8. webRTc实现视频直播

    <!DOCTYPE html> <html> <head> <script type='text/javascript' src='https://cdn.s ...

  9. GDB 调试 .NET 程序实录 - .NET 调用 .so 出现问题怎么解决

    注:本文重要信息使用 *** 屏蔽关键字. 最近国庆前,项目碰到一个很麻烦的问题,这个问题让我们加班到凌晨三点. 大概背景: 客户给了一些 C语言 写的 SDK 库,这些库打包成 .so 文件,然后我 ...

  10. .NET Standard 来日苦短去日长

    作者:Richard 翻译:精致码农-王亮 原文:http://dwz.win/Q4h 自从 .NET 5 宣贯以来,很多人都在问这对 .NET Standard 意味着什么,它是否仍然重要.在这篇文 ...