并发之volatile关键字
volatile关键字
volatile关键字是什么
在上一章我们讲到了并发的的三个概念,那么今天在讲解下在java中可以保证可见性和有序性的一个关键字。
volatile关键字 :当变量的值被该关键字修饰后该值任何读写操作对于其他线程是立即可见的。并且被关键字修饰后的变量被禁止重排序。
volatile原理解析
在定义中我们可以看到volatile关键字有2个特性,可见性和有序性,那么volatile是如何保证这个可见性和有序性的呢?那他为什么不能保证原子性呢?
首先volatile通过加入内存屏障和禁止指令重排序优化来实现的
可见性
写操作
对volatile 变量进行写操作时,会在写操作后添加一个Store Memory Barrier屏障指令将工作内存中的变量写入主内存中。该指令会优先执行在缓冲区所有该变量的相关操作。相当于手动刷新到主内存。
读操作
对volatile变量进行读操作时,会在读操作之前添加一个Load Memory Barrier屏障指令,从主内存读取共享变量。该指令会将失效队列中所有的指令执行,让其他cpu对该变量的改动全部生效,并且刷新回主内存,然后在读取该主内存中的值。相当于手动刷新该变量的最新值。
有序性

- StoreStore屏障 该指令之前的写操作不能和该指令之后的写操作重排序.
- StoreLoad屏障 该指令之前的写操作和该指令之后的读操作重排序.
- LoadLoad屏障 该指令之前的读操作不能和该指令之后的读操作重排序.
- LoadStore屏障 该指令之前的读操作不能和该指令之后的写操作重排序。
写操作
在volatile写和普通写不能交换位置。也就能保证volatile写的值是最新的值。
volatile写以及后来的读不能交换位置,也就是后来的读必须在volatile之后执行。
读操作
而在volatile读不能与在volatile读之后的读操作和写操作交换位置。也就是说volatile读之后的读写操作都必须在volatile读之后完成。
注意:Memory Barrier 可以参看CPU缓存一致性协议MESI-硬件内存模型
volatile适用场景
例子一
@Slf4j
public class UnsafeExample {
private static final int CLIENT_TOTAL = 30000;
private static final int THREAD_TOTAL = 300;
private static volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
//模拟并发
ExecutorService executorService = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(CLIENT_TOTAL);
Semaphore semaphore = new Semaphore(THREAD_TOTAL);
for(int i = 0;i<CLIENT_TOTAL;i++){
executorService.execute(()->{
try{
semaphore.acquire();
add();
semaphore.release();
}catch (Exception e){
e.printStackTrace();
log.error(e.getMessage(),e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println("统计次数:"+count);
}
private static void add(){
count++;
}
}
通过测试得出volatile并不能保证上述demo的线程安全。也就是说依赖当前值来决定下一个值的场景并不适合volatile。
通过上述描述可以总结出两点使用场景。
- 对该变量的操作不依赖当前值。(读自己 写自己)
- 该变量没有包含在具有其他变量的不变式中。 (读别人 写自己)
举个简单的例子
//这种volatile的使用就是错误的 违反了第一条
volatile int x = x+1;
int a = 1;
//这种volatile使用场景是错误的 违反了第二条
volatile int b = a + 1;
实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
因此特别适合作为状态标记量。
使用场景
1.状态标记
重新举一下在上一章中的例子
@Slf4j
public class SimpleHappenBefore {
private static int a = 0;
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
ThreadA threadA = new ThreadA();
ThreadB threadB = new ThreadB();
threadA.start();
threadB.start();
threadA.join();
threadB.join();
a = 0;
flag = false;
}
}
static class ThreadA extends Thread {
@Override
public void run() {
a = 1;
flag = true;
}
}
static class ThreadB extends Thread {
@Override
public void run() {
if (flag) {
a = a * 1;
}
if (a == 0) {
System.out.println("ha,a==0");
}
}
}
}
上面的例子中可以看出flag的改变与其他状态 和他自己本身的状态完全没有关系。所以这里使用volatile关键字是合格的。
2.线程安全的单例对象发布
双重检查单例模式方式发布对象
单例模式-懒汉单例
public class LazySingleton {
private static volatile LazySingleton lazySingleton;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
synchronized(LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
}
3.独立观察(independent observation)
比如论坛常用的统计最后一个注册的用户。
public class UserRegister {
public volatile String lastRegisterUser;
public void registerUser(String user, String password) {
User u = new User();
u.setUser(user);
u.setPassword(password);
userService.registerUser(u);
lastRegisterUser = user;
}
}
4.开销较低的读-写锁策略
读操作使用volatile 写操作使用synchronzied
public class Counter {
private volatile int value;
public int getValue() { return value; }
public synchronized int increment() {
return value++;
}
}
总结
volatile关键字是一个保证可见性和有序性的关键字。该关键字修饰的变量操作与该变量本身状态以及其他变量状态无关的情况下使用才可以保证其并发安全性。顺便吐槽一下(该变量没有包含在具有其他变量的不变式中)这句话,楼主理解这句话理解了很久,后面突然就顿悟了。对于语言文字的理解能力真的很重要。
并发之volatile关键字的更多相关文章
- Java 高效并发之volatile关键字解析
摘录 1. 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执 ...
- Java并发之volatile关键字
引言 说到多线程,我觉得我们最重要的是要理解一个临界区概念. 举个例子,一个班上1个女孩子(临界区),49个男孩子(线程),男孩子的目标就是这一个女孩子,就是会有竞争关系(线程安全问题).推广到实际场 ...
- Java并发之(1):volatile关键字(TIJ21-21.3.3 21.3.4)
Java并发Java服务器端编程的一项必备技能. ** 1 简介 volatile是java中的一个保留关键字,它在英语中的含义是易变的,不稳定的.volatile像final.static等其 ...
- 全面理解Java内存模型(JMM)及volatile关键字(转载)
关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...
- 全面理解Java内存模型(JMM)及volatile关键字
[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772461 出自[zejian ...
- 全面理解Java内存模型(JMM)及volatile关键字(转)
原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...
- volatile关键字的详解-并发编程的体现
xl_echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! 参 ...
- Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- volatile关键字 学习记录2
public class VolatileTest2 implements Runnable{ volatile int resource = 0; public static void main(S ...
随机推荐
- H3C配置FTP服务器
H3C配置FTP服务器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.FTP协议简介 1.FTP协议是互联网上广泛使用的文件传输协议 FTP文件传送协议(File Transf ...
- tomcat配置好后,启动eclipse中的server,不能出现有猫的页面,提示404
原因:tomcat与eclipse中的server未关联起来 解决办法:双击servers中的server,在Server Locations中选中第二项,保存之后再进行刚才的操作就好了.
- mysql常用sql汇总
给一张表新增一个字段 ALTER table student add zz INT() DEFAULT COMMENT '0是授权 1未授权' 给表student 新增一个zz的字段 默认是0 后面是 ...
- 使用Aspose.Cells生成Excel的线型图表
目的: 1.根据模板里面的excel数据信息,动态创建line chart 2.linechart 的样式改为灰色 3.以流的形式写到客户端,不管客户端是否装excel,都可以导出到到客户端 4.使用 ...
- Calendar 日历类的时间操作
我们经常会涉及到对时间的处理,例如登陆网站,我们会看到网站首页显示XXX,欢迎您!今天是XXXX年....某些网站会记录下用户登陆的时间,比如银行的一些网站,对于这些经常需要处理的问题,Java中提供 ...
- 05-迪米特法则(LOD 又名: 最少知道原则)
1. 背景 类与类之间的关系越密切,耦合度越大,当一个类发生变化时,对另一个类的影响也越大. 2. 定义 一个类应该对其它类保持最少的了解. 3. 解决方法 尽量降低类与类 ...
- json 不能 dumps datetime 解决办法
backend.myviews.json_time.py from datetime import date import json from datetime import datetime cla ...
- php 匹配替换中文
1.匹配中文 $str = "中文“; preg_match_all("/[\x{4e00}-\x{9fa5}]+/u",$str,$match); 2.替换中文: 在所 ...
- android logger 日志工具
https://github.com/orhanobut/logger 基础使用:https://blog.csdn.net/github_33304260/article/details/54799 ...
- oracle_使用子查询创建表
create table emp_bk as (select * from emp where 1=2);这句就是复制源表的结构