首先,代码都没有用ide敲,所以不要在意格式,能看懂就行
jmm内存模型:
jmm是什么?

jmm说白了就是定义了jvm中线程和主内存之间的抽象关系的一种模型,也就是线程之间的共享变量存储在主内存,而每个线程都拥有自己的工作内存

happens-befor原则是什么?

在说happens-befor原则之前,我们得先说说jmm的问题所在,如上述所述,每个线程都有自己的一个工作内存,那么我们以一个代码实例来看

public class Test{
int a=1;
public static void main(String []args){
for(int i=0;i<=100000;i++){
new Thread(()->{

a++;

}).start();

}
system.out.println(a);
}

}
OK,大家能看到,这里是有一个开启了100000个线程做自增操作,结果是99130,并不是预期中的100000,那么这是为什么呢?大家都知道,线程是CPU运行的最小单元,那么也就是说,多线程的情况下,cpu会去随机运行的(哪怕是设置了优先级也只是一个权重问题,无法保证强顺序
并且任务一定执行完),所以,因为我们的a++并不是一个原子操作的原因(实际上是4步),也就是说,很可能面临这种情况,当一个线程拿到了cpu的时间切片的时候,首先cpu会将a读到内存中,此时假设a=1然后弄一个临时变量,之后将临时变量增加,之后再返回结构到主内存,但如果在创建了
临时变量但还没有做自增操作的时候,cpu的时间切片突然换到了另一个线程的上面,这个时候这个线程成功做完了自增操作,此时a=2,之后cpu又切回了之前的线程,因为线程里有一个程序计数器,记录了当前线程运行到了哪行代码,所以这个时候第一个线程继续做+1操作,但此时
由于第一个线程的工作内存里的a还是1,所以这个时候线程a在+1之后还是2,之后刷到主内存,此时a=2.所以这两个线程虽然各自运行了一次a++操作,但主内存里的a其实只是加了一次而已.
那么怎么避免这种情况呢?此时就需要我们的happens-befor原则了

happens-befor原则:
1:程序在运行的时候必须按照编写的顺序一样,不能进行指令重排序,指令重排会导致什么后果呢?
public class Test{
int a = 0;
boolean b = false;
public void write(){
a=1;
b = true;
}
public void read(){
if(b){
a = a+1;
}
}
}
而指令重排后可能会是
public class Test{
int a = 0;
boolean b = false;
public void write(){
b = true;
a=1;

}
public void read(){
if(b){
a = a+1;
}
}
}
如果此时有两个线程
new Thread(->(
write();
)).start();
new Thread(->{
read();
}).stert();
假设write()方法线程肯定先于read()方法执行的情况下,此时可能会导致,在b=true的时候,read方法进入,并导致a最后=1,但我们代码的原意其实a=1优先执行的话,a=1的情况会因为read方法的bool没有变成true所以无法进入,因此指令重排已经
干扰到了我们的代码原意了.

那么在什么情况下指令不会重排呢?两种,一种是上下代码有依赖关系如:
int a = 1;
int b = a+1;
此时就不会出现重排;
还有一种就是使用大名鼎鼎的volatile关键字,它利用了内存屏障的性质保证了指令不会重排,最后来点拓展知识,就是long和double这种64字节的数据类型,在读到工作内存的时候不会原子性的,而是每次只读32字节,最后分两次读,但如果加了volatile关键字的话,那么内存屏障
能保证一次性读完64字节.

2:一个锁的解锁,必须要在这个程序的加锁之前,也就是说,我不解锁,那你就别想再加锁,保证了串行
3:对于共享数据,上一个线程对于它的修改,必须要对后续任意操作它的线程可见
4:传递性,假设有三个操作,a,b,c可以理解为a happens befor b,b happens befor c ,那么a happens befor c;

OK,volatile除了内存屏障之外,其实还有另一个作用,就是保证了可见性,它是怎么保证的呢?其实就是将工作内存给去掉,也就是让每次cpu读数据都必须要主内存里里拿,就这样保证在一个线程修改了数据后,他对所有线程都是可见的.可惜的是,这种可见性也并不能保证线程安全,因为线程安全需要两个保证,一个可见性,还有一个原子性.
假设现有一个线程1,一个线程2,一个共享变量a=1,此时线程1将a拿到工作内存做a++操作,在它还没有返回的期间线程b也拿了a到工作内存做++操作,之后不管谁先返回,a都只是做了一次操作而已.所以volatile只能保证那些赋值操作的线程安全,如:Boolean bool = true;

总而言之,volatile的作用就是在操作之间建立happens-befor的关系

最后,推荐大家看下CAS的源码,利用volatile加乐观锁,实现了不需要synchronized也能保证线程安全.

从jmm模型漫谈到happens-befor原则的更多相关文章

  1. JMM模型基础知识笔记

    概述 内存模型可以理解为在特定的操作协议下,对特定的内存或者高速缓存进行读写访问的过程抽象,不同架构下的物理机拥有不一样的内存模型,Java虚拟机也有自己的内存模型,即Java内存模型(JavaMem ...

  2. Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

    JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...

  3. 基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程

    许多以Java多线程开发为主题的技术书籍,都会把对Java虚拟机和Java内存模型的讲解,作为讲授Java并发编程开发的主要内容,有的还深入到计算机系统的内存.CPU.缓存等予以说明.实际上,在实际的 ...

  4. Java并发之内存模型(JMM)浅析

    背景 学习Java并发编程,JMM是绕不过的槛.在Java规范里面指出了JMM是一个比较开拓性的尝试,是一种试图定义一个一致的.跨平台的内存模型.JMM的最初目的,就是为了能够支多线程程序设计的,每个 ...

  5. Java内存模型(JMM)详解

    在Java JVM系列文章中有朋友问为什么要JVM,Java虚拟机不是已经帮我们处理好了么?同样,学习Java内存模型也有同样的问题,为什么要学习Java内存模型.它们的答案是一致的:能够让我们更好的 ...

  6. Java内存模型(JMM)

    JVM与线程(线程在JVM中) 1.JVM什么时候启动?         类被调用时启动,此时会启动JVM线程然后再是其他的线程(main) 2.JVM内存区域 除了程序计数器(PC)之外都有可能发生 ...

  7. Java原理领悟-JMM(java内存模型认知)

    总线锁.缓存锁.MESI缓存一致性协议.CPU 层面的内存屏障 1.JMM定义: Java Memory Model(java内存模型)是一系列的Java虚拟机平台对开发者提供的多线程环境下的内存可见 ...

  8. JMM(Java内存模型)是什么?为什么使用并发?

    1.计算机 首先我们需要讲解下计算机的模型:现代计算机模型是基于-冯诺依曼计算机模型 我们不用管输入和输出设备,最主要的就是中间计算器和存储器之间的交互,也就是CPU与主内存之间取数.存数. 大家会看 ...

  9. 并发编程之volatile与JMM多线程内存模型

    一.通过程序看现象 在开始为大家讲解Java 多线程缓存模型之前,我们先看下面的这一段代码.这段代码的逻辑很简单:主线程启动了两个子线程,一个线程1.一个线程2.线程1先执行,sleep睡眠2秒钟之后 ...

随机推荐

  1. 基于Java实现的冒泡排序算法

    冒泡排序是一种简单基础的排序算法,相信在大学课堂里老师已经讲过了,现在我基于Java来实现一遍. 简述 冒泡排序正如其关键词一样,杂乱的气泡经过浮动,最后大的气泡飘到了上面而小的气泡在下面,无序的元素 ...

  2. 责任链模式和php实现

    职责链模式(又叫责任链模式): 包含了一些命令对象和一些处理对象,每个处理对象决定它能处理那些命令对象,它也知道应该把自己不能处理的命令对象交下一个处理对象,该模式还描述了往该链添加新的处理对象的方法 ...

  3. BZOJ1132: [POI2008]Tro(叉积 排序)

    题意 世上最良心题目描述qwq 平面上有N个点. 求出所有以这N个点为顶点的三角形的面积和 N<=3000 Sol 直接模拟是$n^3$的. 考虑先枚举一个$i$,那么我们要算的就是$\sum_ ...

  4. jq打印

    1.引入jQuery.print.min.js 2.将需要打印的东西用div包起来 3. $(".printDiv").print();

  5. fsck - 检查并修复Linux文件系统

    总览 SYNOPSIS fsck [ -sACVRTNP ] [ -t fstype ] [filesys ... ] [--] [ fs-specific-options ] 描述 DESCRIPT ...

  6. nginx 的编译安装及基本操作

    下载nginx [root@nginx ~]# wget http://nginx.org/download/nginx-1.14.0.tar.gz --2019-05-02 21:52:23-- h ...

  7. 查看cuda版本和cudann

    nvcc -V 没有找到直接查询cudann版本的命令,但发现cudann装在 /usr/local/cuda/lib64/目录下,libcudnn.so就是相应版本

  8. 尺取法 || POJ 2739 Sum of Consecutive Prime Numbers

    给一个数 写成连续质数的和的形式,能写出多少种 *解法:先筛质数 然后尺取法 **尺取法:固定区间左.右端点为0,如果区间和比目标值大则右移左端点,比目标值小则右移右端点               ...

  9. 漫谈Word2vec之skip-gram模型

    https://zhuanlan.zhihu.com/p/30302498 陈运文 ​ 复旦大学 计算机应用技术博士 40 人赞同了该文章 [作者] 刘书龙,现任达观数据技术部工程师,兴趣方向主要为自 ...

  10. 基于mysql数据库 关于sql优化的一些问题

    mysql数据库有一个explain关键词,可以对select语句进行分析并且输出详细的select执行过程的详细信息. 对sql explain后输出几个字段: id:SELECT查询的标识符,每个 ...