一、java内存模型

1.java内存模型

程序运行过程中的临时数据是存放在主存(物理内存)中,但是现代计算机CPU的运算能力和速度非常的高效,从内存中读取和写入数据的速度跟不上CPU的处理速度,在这种情况下,CPU高速缓存应运而生。基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是出现了一个新的问题:目前计算机多为多核CPU或多处理器,每个处理器都有自己的高速缓存,但是这些处理器又共享同一主存。java内存模型主要是定义了java程序中各个变量的访问规则,这里的变量与java编程中变量不是同一个概念,包括:实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,后者是线程私有的,java内存模型规定:

(1)所有的变量存储在主内存中,每个线程都有自己的工作内存,线程的工作内存保存了该线程使用到的变量,并且这些变量是从主内存中对应变量的拷贝

(2)线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量

(3)不同线程之间无法直接访问彼此的工作内存,线程间的变量值的传递需要借助主内存。

2.java工作内存与主内存的交互

lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

二、基本概念

1.可见性:java多线程之间的可见性,一个线程修改的某个对象的状态对另外一个线程是可见的。java中经常使用同步或者volatile来保证变量的可见性。

2.原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。原子代表不可分割性,例如:int a=0,这个操作是不可分割的,反之,a++这个操作是可以切割的
可以分为a=a+1,读取内存中a的值,读取a的值后进行加1操作,把结果值写入内存。

3.有序性:即程序执行的顺序按照代码的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

4.重排序:在执行程序时为了提高性能,编译器和处理器经常会对指令进行重排序,重排序分成三种类型:

  • 编译器优化的重排序。编译器在不改变单线程程序语义放入前提下,可以重新安排语句的执行顺序

  • 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行
  • 内存系统的重排序。由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行

三、volatile

1.基本概念

Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。

2.基本使用

 public class DoubleCheck{

     private static volatile DoubleCheck instance;

     private DoubleCheck(){}

     public static DoubleCheck getInstance(){

         //第一次检测
if (instance==null){
//同步
synchronized (DoubleCheck.class){
if (instance == null){
//多线程环境下可能会出现问题的地方
instance = new DoubleCheck();
}
}
}
return instance;
}
}

上述代码一个经典的单例的双重检测的代码,在单线程环境下这样写并没有什么问题,但如果在多线程环境下就可能出现线程安全问题。原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化,而通过volatile修饰后,就能很好的解决非线程安全的问题。

3.总结

①、保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新

②、禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障

③、volatile的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

④、volatile变量,本质上是通过内存屏障来实现其可见性和禁止重排优化。内存屏障是硬件层的概念,不同的硬件平台实现内存屏障的手段并不是一样,java通过屏蔽这些差异,统一由jvm来生成内存屏障的指令。内存屏障有两个作用:阻止屏障两侧的指令重排序;强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。

Java并发基础--volatile关键字的更多相关文章

  1. 一起来看看java并发中volatile关键字的神奇之处

    并发编程中的三个概念: 1.原子性 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行. 2.可见性 对于可见性,Java提供了volati ...

  2. Java 并发:volatile 关键字解析

    摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...

  3. java并发系列(六)-----Java并发:volatile关键字解析

    在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...

  4. Java并发编程 Volatile关键字解析

    volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...

  5. java并发:volatile关键字

    java并发需要保证原子性,可见性,有序性. http://www.cnblogs.com/expiator/p/9226775.html 一.volatile关键字作用如下: 1.volatile关 ...

  6. Java并发编程volatile关键字

    volatile理解 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和volatile 关键字机制.volatile具有synchronized关键字的“可见性”,vo ...

  7. java并发编程 volatile关键字 精准理解

    1.volatile的作用 一个线程共享变量(类的成员变量.类的静态成员变量等)被volatile修饰之后,就具有以下作用: 1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修 ...

  8. Java 并发编程——volatile与synchronized

    一.Java并发基础 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 这一点可能对于做客户端开发的更加清楚,一般的UI操作都需要开启一个子线程去完成某个任务,否者会容易导致客户 ...

  9. Java 并发基础

    Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...

随机推荐

  1. 设计模式——Spirng_Bean工厂

    前言:对于简单工厂和抽象工厂都有自己的优点和缺点, 比如简单工厂,如果你需要实现的类过多,你最后会出现工厂泛滥的情况,没有有效的控制代码的可重复性.http://www.cnblogs.com/xia ...

  2. 用条件变量实现事件等待器的正确与错误做法--转自陈硕的Blog

    用条件变量实现事件等待器的正确与错误做法 TL;DR 如果你能一眼看出 https://gist.github.com/chenshuo/6430925 中的那 8 个 Waiter classes ...

  3. MyBatis之properties配置

    这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递.例如: <properties resource=" ...

  4. Oracle创建表、修改表、删除表、约束条件语法

    一. 使用create关键字创建表 --(1)创建新表use 数据库(在那个数据库中建表)create table 表名(字段名1(列名) 数据类型 列的特征,字段名2(列名) 数据类型 列的特征(N ...

  5. luajit 64位 for cocos2dx 编译ios解决方法

    最近luajit发布了64位beta版,由于appstore上线必须是64位的应用,而且我的游戏项目用到lua脚本,所以必须要用到64位的luajit来编译lua脚本. 方法如下: 在luajit官网 ...

  6. JS中some(),every(),fiflter(),map()各种循环的区别理解

    1.some():返回一个Boolean,判断是否有元素符合func条件const arr = [1,2,3,4]; arr.some((item)=>{return item>1}) 打 ...

  7. 基于jQuery+JSON的省市县 二级 三级 联动效果

    省市区联动下拉效果在WEB中应用非常广泛,尤其在一些会员信息系统.电商网站最为常见.开发者一般使用Ajax实现无刷新下拉联动.本文将讲述,利用jQuery插件,通过读取JSON数据,实现无刷新动态下拉 ...

  8. HTML5新特性之离线缓存技术

    一.离线缓存的起因. HTML5之前的网页,都是无连接,必须联网才能访问,这其实也是web的特色,这其实对于PC是时代问题并不大,但到了移动互联网时代, 设备终端位置不再固定,依赖无线信号,网络的可靠 ...

  9. 浏览器端用JS实现创建和下载图片

    问题场景 在前端很多的项目中,文件下载的需求很常见.尤其是通过JS生成文件内容,然后通过浏览器端执行下载的操作.如图片,Execl 等的导出功能.日前,项目中就遇到了这类需求,在浏览器端实现保存当前网 ...

  10. 『Python题库 - 简答题』 Python中的基本概念 (121道)

    ## 『Python题库 - 简答题』 Python中的基本概念 1. Python和Java.PHP.C.C#.C++等其他语言的对比? 2. 简述解释型和编译型编程语言? 3. 代码中要修改不可变 ...