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. 一个简洁漂亮的jQuery拖放排序插件DDSort

    拖放排序是WEB应用中常见的功能.虽然网上有很多别人已经造好的轮子,但是就我个人而言,没事就喜欢研究原理,自己造轮子,不管强大与否,简洁够用就是我的目标,再一个就是自己写的东西,应用起来得心应手,修改 ...

  2. Easy Poi入门

    最近有一个需求,就是把excel中的内容,解析成Json对象格式的文件输出. 然后就上网找了一波资料,大神们都说用POI来做.但是我看了一下POI的解析过程,但是为了秉着高效的原则,花最少的时间去实现 ...

  3. Mysql数据库(0)习题分析

    1.查询表中第二高工资的Id,如果没有,返回NULL.此题的关键是如果遇到Empty set,就必须要返回NULL. (1)使用子查询. offset ) AS SecondHighestSalary ...

  4. fenby C语言 P20

    循环停止 break 立刻跳出不再循环 continue立刻跳出循环从下一次循环继续执行 #include <stdio.h> int main(){ int i; for(i=1;i&l ...

  5. 用node实现发送邮箱验证码

    首先,你需要注册一个支持发送的邮箱,我注册是网易邮箱,然后配置smtp. 然后,创建一个node项目,输入npm install nodemailer --save安装邮件依赖. 接着创建一个文件(s ...

  6. python自带的IDLE编译器,听说大神都用这个(附python下载安装教程)

    python这两年这么火,学的人越来越多,小伙伴们都用什么编译器了? 今天教大家安装python并熟悉python自带的编译器IDLE. 第一步,进入python官网https://www.pytho ...

  7. 使用 Github + Hexo 从 0 搭建一个博客

    最近有几位同学在公众号后台留言问我的博客站是怎么建站的,思来想去,还是写一篇从 0 开始吧. 前置准备 我们先聊一下前置准备,可能很多同学一听说要自己搭一个博客系统,直接就望而却步.不得有台服务器么, ...

  8. 大数据之路day02_1--运算符

    运算符这一节主要是介绍算数运算符.赋值运算符.比较运算符.逻辑运算符.三元运算符.接下来一一介绍. 1.算数运算符 ++ 和 -- 的用法 例如:a++ 和 ++a的区别 %的应用场景(取模其实就是取 ...

  9. Elastic Stack 开源的大数据解决方案

    目的 本文主要介绍的内容有以下三点: 一. Elastic Stack是什么以及组成部分 二. Elastic Stack前景以及业务应用 三. Elasticsearch原理(索引方向) 四. El ...

  10. python_day3(文件处理)

    1.文件处理 #Author:Elson Zeng #data = open("test").read() # f = open("test",'a',enco ...