Java内存模型与volatile关键字

一)、并发程序开发

并行程序的开发要涉及多线程、多任务间的协作数据共享问题。

常用的并发控制:内部锁、重入锁、读写锁、信号量。

二)、线程的特点

线程的特点:

1).每一个线程都有一块工作内存区,存放所有线程共享的主内存中变量值的拷贝。

2).当程序执行时,它在自己的工作内存中操作这些变量。

3).线程操作,先获取锁定并清除它的工作内存,重新将共享内存区正确的装入线程

​ 的工作内存区。

4).线程解锁,将线程工作内存区的值重新写回共享内存中。

注:线程的内存操作围绕着线程工作内存区和共享内存区。

三)、线程的操作

线程中java的内存模型:

线程引擎

线程工作内存

主内存

一个线程可以执行的操作

use: 将变量在线程工作内存中的拷贝传送给线程执行引擎。

assign: 将值赋给线程工作内存对应的变量

load: 把read操作从主内存读取的值放入线程工作内存空间

store: 将线程工作区内存的变量内容传送到主内存

lock:

unlock:

主内存可以执行的操作:

read: 把主内存中变量的拷贝内容传送到线程工作内存。

write: 把store操作从线程中得到的值放入主内存的变量拷贝。

lock: 主内存的lock操作使线程获得一个独占锁。

unlock: 主线程的unlock操作使线程释放一个独占锁。

注意事项:

1).use、assign、lock、unlock操作都是线程的执行引擎和线程工作内存的原子操

​ 作:

2).主内存和线程工作内存间的数据传送并不满足原子性:

​ i: 将主内存的数据复制到工作内存区,需要进行两个动作

​ 首先,通过read操作,将主内存中变量的拷贝内容输入到工作内存中,再通过

​ load操作,将读取都内如写到工作内存区中。

​ ii: 从线程工作主内存中写入数据到主内存

​ 首先,通过store操作将线程工作内存中变量的内容输入到主内存中,再通过

​ write操作,将输入的数据写入到主内存。

3).主内存和线程工作内存之间的数据传送需要一定的时间,且消耗的时间不同:

​ 当线程对变量a进行赋值,再给变量b赋值,在另一个线程中,可能先在主内存中

​ 先看见b 的更新,再看见变量a的更新,此时,出现了变量改变,主内存的值更

​ 新不及时的情况。

原因:主线程和工作区的数据传送需要一定的时间,且所需时间不同,当变量做

​ 出了改变,不能很快的将改变的值反应到主内存中,线程中共享变量的值

​ 都是从主内存中取出的。

4).线程的每一个read操作都跟随着一个load操作,每一个store操作都跟着一个

​ write操作。

四)、double和long类型的非原子变量处理

double 和 long类型的特点:

在变量进行write和read操作时,主内存把它当做两个32位的read或write操作处理,且这两个操作在时间上是分开的,对第一个32位进行操作后,还有其它的操作介于它们之间。

举例:

两个并发的线程对共享的double或long型变量赋不同的值,最后变量得到的值可能

不等于任何一个线程所赋的值,而可能是依赖于具体应用的两个线程所赋值的混合

赋值分析:

第一个同步线程对double的第一个32位进行赋值,输入到线程工作内存,中间隔着一系列操作,此时第一个32位可能已经写入了主内存,随着第二个32位也完成了赋值,但未写入都主内存,紧着这第二个线程获取到锁,对同一个变量进行操作,开始第一个32位的赋值操作,并将这个32位写入主内存,此时,double能量的值是两个同步线程所赋值的混合。

改进:必须对double或long进行同步,使用volatile关键字。

五)、 Volatile关键字

为什么要使用volatile关键字?

每个线程都有自己的工作区,当一个线程改变自己工作内存中的数据时,对其他线程来说是不可见的,使用volatile关键字,迫使所有的线程均读写主内存中对应变量,从而使得volatile变量在多线程间可见。

volatile的作用:

1).其他线程对变量的修改,可以及时反应在当前线程中。

​ 原因:使用volatile标识变量,迫使所有线程均读写主内存中对应的变量

2).确保当前线程对volatile变量的修改,能及时写回共享主内存中,并被其他线程

​ 所见。

3).使用volatile声明的变量,编译器保证其有序性。

六)、使用例子来说明Volatile关键字的作用

构建两个线程,操作同一个VolatileTest02的变量isExist,一个线程根据 isExist == !isExist来判断程序是否执行退出操作,另一个不断切换isExist值的。

Boolean is isExist = false;

tryExist()

swapExist()

import org.junit.Test;

/**
* 使用一个boolena型变量,isExist来验证Volatile
*
*
*/
public class VolatileTest02 {
private Boolean isExist = false; /**
* 尝试退出线程
* 读取非Volatile修饰的变量,从当前的工作线程空间读取,不能及时的发现另一个线程对变量值isExist的修改
*
*/
public void tryExist(){
if(isExist == !isExist){
System.out.println("2");
System.exit(0);
}
} /**
* 更改isExist的值
* 当isExist的值修改时,被写入主内存中。
* 此时,mainThread线程的线程工作内存未能及时的读取isExist的更改,没有将改变值load入线程工作内存,isExist == !isExist很难成立
*
*/
public void swapVlaue(){
isExist = !isExist;
} /**
* 创建两个线程,一个线程用于不断的执行是否退出操作,另一个线程不断的更改isExist的值
*/
@Test
public void test() throws InterruptedException {
final VolatileTest02 volObj = new VolatileTest02();
Thread mainThread = new Thread(){
@Override
public void run(){
//不断判断isExist的值,如果isExist = !isExist,程序退出
System.out.println("mainThread start");
//线程不断的尝试退出
while(true){
volObj.tryExist();
} }
};
mainThread.start(); Thread swapThread = new Thread(){
public void run() {
System.out.println("swapThread start");
//线程不断的修改isExist的值
while (true) {
volObj.swapVlaue();
System.out.println(1);
} }
};
swapThread.start();
Thread.sleep(1000);
}
}

案例分析:

mainThread线程:

开启该线程,判断isExist == !isExist, 因为读取isExist变量是从当前线程的工作内存中读取,虽然使用tryExist()不断改变isExist的值,但不是对mainThread线程立即可见的,期间还要经历read/load过程。

swapThread线程:

开启改线程,tryExist(),isExist = !isExist,不断改变isExist的值,将值写入到主内存

当该线程的isExist被修改时,不能及时的反应到mainThread中。

使用volatile关键字:

线程直接从主内存中读取数据,isExist == !isExist, 当读取等式的左边数据后,右边数据前,isExist值及有可能已经被修改,此时isExist == !isExist成立。

volatile关键字的作用:

1).确保当前线程对volatile的更改,能及时的写回共享主内存。

2).其他线程对变量的修改,可以及时反映在当前线程中。

3).使用volatile声明的变量,保证其有序性

​ int number = 0;

int stop = false;

未使用volatile可能会先将stop的值写入到,再将number值写入

Java内存模型与volatile关键字的更多相关文章

  1. Java内存模型中volatile关键字的作用

    volatile作用总结: 1. 强制线程从公共内存中取得变量的值,而不是从线程的私有的本地内存中,volatile修饰的变量不具有原子性(修改一个变量的值不能同步). 2. 保证volatile修饰 ...

  2. Java并发编程:JMM(Java内存模型)和volatile

    1. 并发编程的3个概念 并发编程时,要想并发程序正确地执行,必须要保证原子性.可见性和有序性.只要有一个没有被保证,就有可能会导致程序运行不正确. 1.1. 原子性 原子性:即一个或多个操作要么全部 ...

  3. Java内存模型:volatile详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt202 Java内存模型:volatile是干什么用的Volatile字段是用 ...

  4. Java内存模型与Volatile,Happen-Before原则等

     Java的内存模型 Java内存模型(JMM)是一个抽象的模型.决定了线程主要定义了线程和内存间的抽象关系:主内存存放的是线程共享变量,每个线程有自己的工作内存,存放变量的副本,只能对副本进行读写, ...

  5. Java内存模型以及Volatile、Synchronize关键字的疑问

    1.众所周知,java的内存模型是一个主内存,每个线程都有一个工作内存空间,那么主内存同步到工作内存是什么时候发生的呢?工作内存同步会主内存又是什么时候发生的呢? 在cpu进行线程切换时就会发生这些同 ...

  6. 【java】java内存模型(2)--volatile内存语义详解

    多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”.可见性的意思是当一个线程 ...

  7. JMM 内存模型 与 volatile 关键字

    内存模型 线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory). 本地内存中存储了该线程以读/写共享变量的副本. 不同线程之间无法相互 ...

  8. java内存模型与volatile变量与Atomic的compareAndSet

    java分主内存和工作内存, 主内存是线程共享的, 工作内存是每个线程独有的. java对主内存的操作是通过工作内存间接完成的: 先拷贝主内存变量值到工作内存, 在工作内存操作这个变量的副本, 完成后 ...

  9. 并发编程之 Java 内存模型 + volatile 关键字 + Happen-Before 规则

    前言 楼主这个标题其实有一种作死的味道,为什么呢,这三个东西其实可以分开为三篇文章来写,但是,楼主认为这三个东西又都是高度相关的,应当在一个知识点中.在一次学习中去理解这些东西.才能更好的理解 Jav ...

随机推荐

  1. 4. NFS存储服务器搭建

    1.什么是NFS? Network file system 网络文件系统 nfs共享存储 2.nfs能干什么? nfs 能为 不同主机系统之间 实现 文件的共享 3.为什么要使用nfs? 在集群架构中 ...

  2. Java基础(二十六)Java IO(3)字节流(Byte Stream)

    字节流是以字节为单位来处理数据的,由于字节流不会对数据进行任何转换,因此用来处理二进制的数据. 一.InputStream类与OutputStream类 1.InputStream类是所有字节输入流的 ...

  3. Java基础(二十五)Java IO(2)文件File类

    File类是一个与流无关的类.File类的对象可以获取文件及其文件所在的目录.文件的长度等信息. 1.File对象的常用构造方法. (1)File(String pathname) File file ...

  4. SpringBoot整合Mybatisplus3.x之CRUD(一)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  5. ubuntu19.10桌面版

    一:安装ubuntu卡住,英伟达显卡兼容性问题 解决方法: 将 quite splash --- 改为 quite splash nomodeset 二:从零开始配置ubuntu19.10 sudo ...

  6. 机器学习笔记(一)&#183; 感知机算法 &#183; 原理篇

    这篇学习笔记强调几何直觉,同时也注重感知机算法内部的动机.限于篇幅,这里仅仅讨论了感知机的一般情形.损失函数的引入.工作原理.关于感知机的对偶形式和核感知机,会专门写另外一篇文章.关于感知机的实现代码 ...

  7. xml 需要转义的字符

    XML实体中不允许出现"&","<",">"等特殊字符,否则XML语法检查时将出错,如果编写的XML文件必须包含这些字符 ...

  8. linux(CentOS release 6.5)环境搭建svn

    正文之前,说几句关于svn和git的闲话. 之前用的版本控制工具主要都是svn,随着时间的推移,git以其强大灵活的分支管理功能受到大众喜爱.尤其是多人同时开发时同一项目,且不同部分功能时,git的分 ...

  9. Web for pentester_writeup之XML attacks篇

    Web for pentester_writeup之XML attacks篇 XML attacks(XML攻击) Example 1 - XML外部实体注入(XXE) Payload http:// ...

  10. Python进阶函数

    一.函数的动态参数 之前我们说过了传参, 如果我们需要给一个函数传参, 而参数又是不确定的. 或者我给一个函数传很多参数, 我的形参就要写很多, 很麻烦, 怎么办呢. 我们可以考虑使用动态参数. 动态 ...