volatile关键字的作用
引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识。网上关于volatile的文章非常多,但是并没有讲解非常详细的文章。(哪位要是有好的资料麻烦共享一份给我!)多数的都是一些理论讲解,没有实际的例子代码,就算有代码的也测试不出效果,总之理论总是与代码不匹配。
后来在我不懈的努力之下总算研究出一些成果,在此分享给大家!如果大家发现有错误的地方欢迎大家指正,谢谢!
在Java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉。
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块(synchronized) 和 volatile 关键字机制。
synchronized(不做过多解释)
同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用
synchronized 修饰的方法 或者 代码块。
volatile
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。
如果要深入了解volatile关键字的作用,就必须先来了解一下JVM在运行时候的内存分配过程。
在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,
线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存
变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,
在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图
描述这写交互!

那么在了解完JVM在运行时候的内存分配过程以后,我们开始真正深入的讨论volatile的具体作用
请看代码:

1 public class VolatileTest extends Thread {
2
3 boolean flag = false;
4 int i = 0;
5
6 public void run() {
7 while (!flag) {
8 i++;
9 }
10 }
11
12 public static void main(String[] args) throws Exception {
13 VolatileTest vt = new VolatileTest();
14 vt.start();
15 Thread.sleep(2000);
16 vt.flag = true;
17 System.out.println("stope" + vt.i);
18 }
19 }

上面的代码是通过标记flag来控制VolatileTest线程while循环退出的例子!
下面让我用伪代码来描述一下我们的程序
- 首先创建 VolatileTest vt = new VolatileTest();
- 然后启动线程 vt.start();
- 暂停主线程2秒(Main) Thread.sleep(2000);
- 这时的vt线程已经开始执行,进行i++;
- 主线程暂停2秒结束以后将 vt.flag = true;
- 打印语句 System.out.println("stope" + vt.i); 在此同时由于vt.flag被设置为true,所以vt线程在进行下一次while判断 while (!flag) 返回假 结束循环 vt线程方法结束退出!
- 主线程结束
上面的叙述看似并没有什么问题,“似乎”完全正确。那就让我们把程序运行起来看看效果吧,执行mian方法。2秒钟以后控制台打印stope-202753974。
可是奇怪的事情发生了 程序并没有退出。vt线程仍然在运行,也就是说我们在主线程设置的 vt.flag = true;没有起作用。
在这里我需要说明一下,有的同学可能在测试上面代码的时候程序可以正常退出。那是因为你的JVM没有优化造成的!在DOC下面输入 java -version 查看 如果显示Java HotSpot(TM) ... Server 则JVM会进行优化。
如果显示Java HotSpot(TM) ... Client 为客户端模式,需要设置成Server模式 设置方法问Google

问题出现了,为什么我在主线程(main)中设置了vt.flag = true; 而vt线程在进行判断flag的时候拿到的仍然是false?
那么按照我们上面所讲的 “JVM在运行时候的内存分配过程” 就很好解释上面的问题了。
首先 vt线程在运行的时候会把 变量 flag 与 i (代码3,4行)从“主内存” 拷贝到 线程栈内存(上图的线程工作内存)
然后 vt线程开始执行while循环
7 while (!flag) {
8 i++;
9 }
while (!flag)进行判断的flag 是在线程工作内存当中获取,而不是从 “主内存”中获取。
i++; 将线程内存中的i++; 加完以后将结果写回至 "主内存",如此重复。
然后再说说主线程的执行过程。 我只说明关键的地方
vt.flag = true;
主线程将vt.flag的值同样 从主内存中拷贝到自己的线程工作内存 然后修改flag=true. 然后再将新值回到主内存。
这就解释了为什么在主线程(main)中设置了vt.flag = true; 而vt线程在进行判断flag的时候拿到的仍然是false。那就是因为vt线程每次判断flag标记的时候是从它自己的“工作内存中”取值,而并非从主内存中取值!
这也是JVM为了提供性能而做的优化。那我们如何能让vt线程每次判断flag的时候都强制它去主内存中取值呢。这就是volatile关键字的作用。
再次修改我们的代码

public class VolatileTest extends Thread {
volatile boolean flag = false;
int i = 0;
public void run() {
while (!flag) {
i++;
}
}
public static void main(String[] args) throws Exception {
VolatileTest vt = new VolatileTest();
vt.start();
Thread.sleep(2000);
vt.flag = true;
System.out.println("stope" + vt.i);
}
}

在flag前面加上volatile关键字,强制线程每次读取该值的时候都去“主内存”中取值。在试试我们的程序吧,已经正常退出了。
https://www.cnblogs.com/daxin/p/3364014.html
volatile关键字的作用的更多相关文章
- Java并发编程学习笔记 深入理解volatile关键字的作用
引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识.网上关于volatile的 ...
- volatile关键字的作用、原理
在只有双重检查锁,没有volatile的懒加载单例模式中,由于指令重排序的问题,我确实不会拿到两个不同的单例了,但我会拿到"半个"单例. 而发挥神奇作用的volatile,可以当之 ...
- 面试题:volatile关键字的作用、原理
在只有双重检查锁,没有volatile的懒加载单例模式中,由于指令重排序的问题,我确实不会拿到两个不同的单例了,但我会拿到“半个”单例. 而发挥神奇作用的volatile,可以当之无愧的被称为Java ...
- Java中volatile关键字及其作用是什么?
在 Java 多线程中如何保证线程的安全性?那我们可以使用 Synchronized 同步锁来给需要多个线程访问的代码块加锁以保证线程安全性.使用 synchronized 虽然可以解决多线程安全问题 ...
- Java 中 volatile 关键字及其作用
引言 作为 Java 初学者,几乎从未使用过 volatile 关键字.但是,在面试过程中,volatile 关键字以及其作用还是经常被面试官问及.这里给各位童靴讲解一下 volatile 关键字的作 ...
- const,static,volatile关键字的作用
const关键字: 1.欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了: 2.对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为 ...
- Java内存模型中volatile关键字的作用
volatile作用总结: 1. 强制线程从公共内存中取得变量的值,而不是从线程的私有的本地内存中,volatile修饰的变量不具有原子性(修改一个变量的值不能同步). 2. 保证volatile修饰 ...
- 多线程中volatile关键字的作用
原文链接:https://blog.csdn.net/xuwentao37x/article/details/27804169 多线程的程序是出了名的难编写.难验证.难调试.难维护,这通常是件苦差事. ...
- volatile关键字的作用-respect
volatile关键字的含义? volatile定义的变量可能会意外的改变,改变它的情况有很多(例如:操作系统,硬件,线程),编译就不会去假设这个值,也就是说每次访问这个变量时,系统就会小心翼翼的去从 ...
随机推荐
- spring拦截器-过滤器的区别
1. 理解 拦截器 :是在面向切面编程的时候,在你的 service 或者一个方法前调用一个方法,或者在方法后调用一个方法:比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业 ...
- 唯美MACD-完全版
前言: 自己很喜欢MACD这个指标,因为很欠缺所以就搜集的多一点,有人问,学习缠为什么还这么搜集些Macd的资料呢?因为在分析走势(或盘整背驰.或趋势背驰)的时候我的习惯使用Macd做辅助判断,所以M ...
- java框架之SpringCloud(6)-Zuul路由网关
介绍 Zuul 包含了对请求的路由和过滤两个最重要的功能: 其中路由功能服务将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础.而过滤的功能则负责对请求的处理过程进行干预,是实现请求校验 ...
- Intellij Idea debug 模式如果发现异常,即添加异常断点在发生异常处
以前用eclipse的时候,可以根据所抛出的异常进行调试,比如:出现了空指针异常,我想知道是哪一行抛出的,在eclipse中我只需在debug模式下把空指针异常这个名字设置进去,当遇到空指针异常时,e ...
- Django框架详细介绍---视图系统
Django视图系统 1.什么是视图 在Django中,一个视图函数/类,称为视图.实质就是一个用户自定义的简单函数,用来接收WEB请求并xing响应请求,响应的内容可以是一个HTML文件.重定向.一 ...
- Shell脚本:while read line无法读取最后一行的问题
[1]Shell脚本:while read line无法读取最后一行的问题 刚刚利用shell脚本处理日志文件时,发现了一个问题:while read line无法读取到最后一行 通过编辑器可以看到待 ...
- j2ee高级开发技术课程第六周
一.jsf(java server faces)的运行原理(工作方式) 1.jsf应用是事件驱动的,当一个事件发生时(比如用户单击一个按钮),事件通知通过HTTP发往服务器,服务器端使用叫做Faces ...
- Android项目开发第二天,关于GitHub
一. 今天在网上学习了如何使用GitHub,了解了GitHub是干什么的. 作为开源代码库以及版本控制系统,Github拥有超过900万开发者用户.随着越来越多的应用程序转移到了云上,Github已经 ...
- PHP 订单延时处理:延迟队列(未鉴定)
PHP 订单延时处理:延迟队列: https://github.com/chenlinzhong/php-delayqueue
- Java的类继承
知识点1.继承作用:提高代码的重用性,继承之后子类可以继承父类中的属性和方法减少重复代码条件:子类和父类要满足is a的逻辑关系,才能使用继承.如:苹果 is a水果语法:使用extends 连接子类 ...