1、volatile的作用

  一个线程共享变量(类的成员变量、类的静态成员变量等)被volatile修饰之后,就具有以下作用:

  1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修改了某个变量的值,则该新值对其他线程立即可见(可立即访问新值/立即强制写入主存);

  2)禁止指令重排(包括java编译器和CPU运行时指令重排序);

3)禁用缓存(java虚拟机规范)---子线程的工作内存(包括了CPU缓存)。

2、相关概念

2.1)指令重排序:

(1)java编译器运行时指令重排序(实质是JVM的优化处理),java源码编译生成class文件后,JVM需要在运行时(runtime)将字节码文件(class文件)转化为操作系统能够执行的指令(JIT编译器),在转换的过程中jvm会对指令进行优化调整,以提高运行效率。

(2)CPU运行时指令重排序,cpu优化的方式,为避免处理器访问主内存的时间开销,处理器采用缓存机制(三层缓存)提高性能(缓存之间的数据一致性遵循协议规范),当CPU写缓存时,发现缓存区块正被其他CPU占用,为了提高CPU的处理性能,可能将后面的读缓存命令优先执行。

 2.2)java内存模型规范:

线程要操作共享变量时,需要从主内存读取到工作内存,改变值后需要从工作内存同步到主内存中。多线程的情况下,同步到主内存时遵循同步协议规范。

3、相关现象分析

3.1)先看一段代码,

public class VolatileInfo {

	private static boolean flag = true;

	public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
long end = 0;
int index = 0;
while (VolatileInfo.flag) {
index++;
end = System.currentTimeMillis();
if ((end-start)/1000==5) {//5秒之后结束任务
break;
}
}
System.out.println("index:"+index);
System.out.println("cost:"+(end-start)/1000+"s");
}
}).start(); try {
TimeUnit.SECONDS.sleep(2);//阻塞线程2秒
} catch (InterruptedException e) {
e.printStackTrace();
} VolatileInfo.flag = false;
System.out.println("条件置为false");
}
}

  输出结果:

条件置为false
index:269460217
cost:5s

  耗时5秒,并不是2秒后停止(注:静态变量flag用volatile 修饰后执行时间是2秒)。

3.2)问题分析:

1)主线程阻塞后修改的flag的值并没有及时写入到主存,子线程没有及时读取到flag值,导致循环继续执行---即存在缓存;

2)指令重排序,先从主内存中执行读的操作,陷入死循环,不再向主内存写入。

3.3)解决办法:

1)flag变量使用volatile关键词修饰;

  2)使用Synchronized加同步锁(从主存中直接读取,相当于禁用缓存,参考4.3);

	while (VolatileInfo.flag) {
synchronized (this) {
index++;
}
end = System.currentTimeMillis();
if ((end - start) / 1000 == 5) {// 5秒之后结束任务
break;
}
}

3)从eclipse中将jvm执行class文件的方式改为client(默认是server模式,jvm进行了一些优化调整);

4)从eclipse中配置添加关闭server优化的参数---此处请自行百度^_^;              ---------- 禁止指令重排。

4、扩展知识点

4.1)java内存模型多线程情况下的工作内存和主内存同步协议的8种原子操作:

  lock(锁定):作用于主内存,将主内存中的变量锁定,为一个线程所独有;

unlock(解锁):作用于主内存,解除lock的锁定,释放后的变量能够被其他线程访问;

  read(读取):作用于主内存,将主内存中的变量读取到工作内存中,以便随后的load动作使用;

  load(载入):作用于工作内存,它把read读取的值保存到工作内存中的变量副本中;

  use(使用):作用于工作内存,它把工作内存中的值传递给线程代码执行引擎;

  assign(赋值):作用于工作内存,它把从执行引擎处理返回的值重新赋值给工作内存中的变量;

  store(存储):作用于工作内存,将变量副本中的值传送到主内存中,以备随后的write操作使用;

  write(写入):作用于主内存,它把store传送值写到主内存的共享变量中。

4.2)java内存模型操作规范:

  1)将一个变量从主内存复制到工作内存要顺序依次(不一定连续)执行read、load操作;

2)做了assign操作,必须同步回主内存等。

4.3)保证线程共享变量可见性的方式:

  1)用final修饰的变量

2) Synchronized 同步锁

    Synchronized规范, 进入同步块前,先清空工作内存中的共享变量,从主内存中重新加载; 解锁前必须把修改的共享变量同步回主内存。

    锁机制,锁机制保护共享资源,只有获得锁的线程才能操作共享资源。

3) 用volatile修饰的变量

    volatile语义规范,使用volatile修饰的变量时,必须从主内存中加载,并且read、load是连续的;修改volatile修饰的变量时,必须立即同步到主内存,并且store、write是连续的。

一字一句敲的,支持原创,转载请注明出处,谢谢:https://www.cnblogs.com/huyangshu-fs/p/10225898.html

java并发编程 volatile关键字 精准理解的更多相关文章

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

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

  2. Java并发编程volatile关键字

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

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

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

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

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

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

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

  6. Java并发编程学习笔记 深入理解volatile关键字的作用

    引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识.网上关于volatile的 ...

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

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

  8. Java并发编程--Volatile详解

    摘要      Volatile是Java提供的一种弱同步机制,当一个变量被声明成volatile类型后编译器不会将该变量的操作与其他内存操作进行重排序.在某些场景下使用volatile代替锁可以减少 ...

  9. Java并发编程-volatile可见性的介绍

    要学习好Java的多线程,就一定得对volatile关键字的作用机制了熟于胸.最近博主看了大量关于volatile的相关博客,对其有了一点初步的理解和认识,下面通过自己的话叙述整理一遍. 有什么用? ...

随机推荐

  1. springboot mybatis自定义枚举enum转换

    原文链接:https://blog.csdn.net/u014527058/article/details/62883573 一.概述 在利用Spring进行Web后台开发时,经常会遇到枚举类型的绑定 ...

  2. 内核启动后,lcd显示logo失败

    针对-s5pv210,但对其他平台也使用 lcd显示logo失败,若显示成功默认的logo是一只企鹅,但是串口打印“Start display and show logo”,但是LCD屏没有显示    ...

  3. [POI2014]KUR-Couriers BZOJ3524 主席树

    给一个长度为n的序列a.1≤a[i]≤n. m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2.如果存在,输出这个数,否则输出0. Input 第一行两 ...

  4. [USACO08OCT]牧场散步Pasture Walking BZOJ1602 LCA

    题目描述 The N cows (2 <= N <= 1,000) conveniently numbered 1..N are grazing among the N pastures ...

  5. POJ 3734 Blocks(矩阵快速幂+矩阵递推式)

    题意:个n个方块涂色, 只能涂红黄蓝绿四种颜色,求最终红色和绿色都为偶数的方案数. 该题我们可以想到一个递推式 .   设a[i]表示到第i个方块为止红绿是偶数的方案数, b[i]为红绿恰有一个是偶数 ...

  6. day_04 列表

    1. 列表list 能装对象的对象,有序的(按照我们存放的顺序) 以[]表示,里面可以存放大量各种元素,各个元组用逗号隔开 列表也具有索引和切片 2. 列表的增改删查 1. 增 1.append() ...

  7. CHUCK手把手带你搞定OPENSTACK

    一.OpenStack初探 1.1 OpenStack简介 OpenStack是一整套开源软件项目的综合,它允许企业或服务提供者建立.运行自己的云计算和存储设施.Rackspace与NASA是最初重要 ...

  8. 斑马打印机客户端GET和POST,以及后端两种打印方式。

    斑马打印机客户端GET和POST,以及后端两种打印方式. 背景环境:打印机安装在客户端外网.当用户登录时,通过ajax取服务器数据,返回打印机命令,然后客户端通过JS发送给斑马打印机. 1.使用Get ...

  9. 练习六十八:pyecharts模块练习

    用一个地图显示最热的几个城市温度 代码(用Geo模块) from pyecharts import Geo attr = ["西安","上海","苏州 ...

  10. sublime快捷键:快速查找函数和快速匹配括号

    1. 快速查找函数 Ctrl+R 2. 快速匹配括号 光标置于括号中,Ctrl+Shift+M 快速匹配括号内容,再按下 Ctrl+Shift+[ 折叠代码, Ctrl+Shift+] 展开代码. 3 ...