volatile能保持线程安全吗_volatile是什么?volatile能保证线程安全性吗?如何正确使用volatile?...
1. volatile是什么?
在谈及线程安全时,常会说到一个变量——volatile。在《Java并发编程实战》一书中是这么定义volatile的——“Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程”。这句话说明了两点:①volatile变量是一种同步机制;②volatile能够确保可见性。这两点和我们探讨“volatile变量是否能够保证线程安全性”息息相关。
2. volatile变量能确保线程安全性吗?为什么?
什么是同步机制?在并发程序设计中,各进程对公共变量的访问必须加以制约,这种制约称为同步。也就是说,同步机制即为对共享资源的一种制约。那么问题来了:volatile这种“稍弱的同步机制”是怎么制约各个进程对共享资源的访问的呢?答案就在“volatile能够确保可见性”中。
2.1 可见性
volatile能够保证字段的可见性:volatile变量,用来确保将变量的更新操作通知到其他线程。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
可见性和“线程如何对变量进行操作(取值、赋值等)”有关系:
我们要先明确一个定律:线程对变量的所有操作(取值、赋值等)都必须在工作内存(各线程独立拥有)中进行,而不能直接读写内存中的变量,各工作内存间也不能相互访问。对于volatile变量来说,由于它特殊的操作顺序性规定,看起来如同操作主内存一般,但实际上 volatile变量也是遵循这一定律的。
关于主存与工作内存之间具体的交互协议(即一个变量如何从主存拷贝到工作内存、如何从工作内存同步到主存等实现细节),Java内存模型中定义了以下八种操作来完成:
lock:(锁定),unlock(解锁),read(读取),load(载入),use(试用), assign(赋值),store(存储),write(写入)。
volatile 对这八种操作有着两个特殊的限定,正因为有这些限定才让volatile修饰的变量有可见性以及可以禁止指令重排序 :
① use动作之前必须要有read和load动作, 这三个动作必须是连续出现的。【表示:每次工作内存要使用volatile变量之前必须去主存中拿取最新的volatile变量】
② assign动作之后必须跟着store和write动作,这三个动作必须是连续出现的。【表示: 每次工作内存改变了volatile变量的值,就必须把该值写回到主存中】
有以上两条规则就能保证每个线程每次去拿volatile变量的时候,那个变量肯定是最新的, 其实也就相当于好多个线程用的是同一个内存,无工作内存和主存之分。而操作没有用volatile修饰的变量则不能保证每次都能获取到最新的变量值。
2.2 所以volatile究竟能否保证线程安全性?
答:在某些特定的情况下能。那到底什么是什么能,什么时候又不能了呢?我们继续往下看。
(1)volatile能保证线程安全的情况
要使 volatile 变量提供理想的线程安全性,必须同时满足两个条件:①对变量的写操作不依赖于当前值。②该变量没有包含在具有其他变量的不变式中。
实际上,这两个条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。大多数编程情形都会与这两个条件的其中之一冲突(如:“若没有则添加”、“若相等则移出”的复合操作等复合操作都是与①或②相冲突的),使得 volatile 变量不能像 synchronized 那样普遍适用于实现线程安全。
(2)volatile不能保证线程安全的情况
除了(1)中提到的能够使volatile发挥保证线程安全性的情况,其他情况下volatile并不能保证线程安全问题,因为volatile并不能保证变量操作的原子性。
我们先以 i++( i++ 是非原子操作)为例:
private volatile int i = 0,两个线程同时执行 i++,
此时是两个线程同时从主内存中拿到 i 的最新值 0 ,并且同时对 i 进行 +1 操作并将和赋值回 i,最后同时将 +1 后的 i 值写回主内存中,最终 i == 1,很明显结果是错的。
下面我们再通过更详细的代码来验证“即使变量用了volatile来修饰,才进行非原子操作时依旧会出现线程安全问题”:
class Window implementsRunnable {private volatile int ticket = 100;public voidrun() {for(;;) {//通过下面的①②两个步骤我们可以发现:对一个共享资源可以多个线程同时进行修改,自然就会有线程安全问题。
if (ticket > 0) {try{
Thread.sleep(100);//①多个线程同时判断到“ticket>0”,然后挂起了
} catch(InterruptedException e) {
e.printStackTrace();
}//②多个线程同时醒来,同时进行“ticket--”操作:
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
}else{break;
}
}
}
}
public classA03UseVolatileIsNotThreadSafe {public static voidmain(String[] args) {
Window w= newWindow();
Thread t1= newThread(w);
Thread t2= newThread(w);
Thread t3= newThread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
测试结果:(1)出现了大量的重复数字; (2)最后还输出了 “-1”;==》说明变量即使用volatile修饰了但依旧出现了线程安全问题。
代码解析:
出现问题(1)的原因:线程存在“先检查后执行”的竞态条件。可能有两个线程同时拥有CPU的执行权(机器是双核的),它们判断到做“if (ticket > 0)”,并同时做“ticket--”操作。
出现问题(2)的原因:
①当ticket==1时,两个或多个线程同时通过了“if (ticket > 0)”的判断,并进入了判断框中去执行代码;
②然后它们执行到“Thread.sleep(100);”就睡了;
③睡醒后总有一个线程会先抢到cup的执行权,然后执行“ticket--”操作,并将最新的ticket数值推送告知到每个线程;
④此时那些在判断框中的其他的线程并不会再次做“if (ticket > 0)”的判断,而是直接拿最新的ticket并做“ticket--”操作。
就算线程在“ticket--”之前每次都做“if (ticket > 0)”的判断,也依旧会有线程安全问题,因为又可能出现①那种同时通过判断的状态。
// volatile的典型用法:检查某个状态标记,以判断是否退出循环。
volatile booleanasleep;
……
while( !asleep){
countSomeSheep();
}
3. volatile的典型用法:检查某个状态标记,以判断是否退出循环。
volatile booleanasleep;
……while( !asleep){
countSomeSheep();
}
4.总结:因为volatile不能保证变量操作的原子性,所以试图通过volatile来保证线程安全性是不靠谱的。
volatile能保持线程安全吗_volatile是什么?volatile能保证线程安全性吗?如何正确使用volatile?...相关推荐
- 面试准备每日系列:计算机底层之并发编程(一)原子性、atomic、CAS、ABA、可见性、有序性、指令重排、volatile、内存屏障、缓存一致性、四核八线程
文章目录 1. 什么是进程?什么是线程? 2. 线程切换 3. 四核八线程是什么意思 3.1 单核CPU设定多线程是否有意义 4. 并发编程的原子性 4.1 如何解决原子性问题 & atomi ...
- Java中保证线程安全的三板斧
前言 现在,如果要使用 Java 实现一段线程安全的代码,大致有 synchronized . java.util.concurrent 包等手段.虽然大家都会用,但却不一定真正清楚其在 JVM 层面 ...
- Java线程池状态判断源码_深入浅出Java线程池:源码篇
前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...
- 线程安全list_多线程开发之如何创建一个线程安全的类
上一篇讨论了如何解决线程安全的问题,今天总结如何设计一个线程安全的类: 创建线程安全类的关注点 一个类要想线程安全,除了上一篇文章通过外部解决方式外,还可以通过合理的设计类的内部来解决,使类本身就线程 ...
- 多线程情况下如何保证线程安全
一.线程安全等级 其实线程安全并不是一个"非黑即白"单项选择题.按照"线程安全"的安全程度由强到弱来排序,我们可以将java语言中各种操作共享的数据分为以下5类 ...
- 到底如何保证线程安全,总结得太好了。。
一.线程安全等级 之前的博客中已有所提及"线程安全"问题,一般我们常说某某类是线程安全的,某某是非线程安全的.其实线程安全并不是一个"非黑即白"单项选择题. 按 ...
- volatile能保持线程安全吗_Java线程安全(volatile synchronized)
总结 volatile不能保证线程安全而synchronized可以保证线程安全.volatile只能保证被其修饰变量的内存可见性,但如果对该变量执行的是非原子操作线程依旧是不安全的.而synchro ...
- Java并发,volatile+不可变容器对象能保证线程安全么?!
<Java并发编程实战>第3章原文 <Java并发编程实战>中3.4.2 示例:使用Volatile类型来发布不可变对象 在前面的UnsafeCachingFactorizer ...
- java和线程相关的关键字有哪些_Java中有哪些机制来保证线程安全?synchronized关键字和volatile关键字...
想要解决线程安全问题,首先要知道为什么会造成线程不安全? 在单线程中,我们从来没有提到个线程安全问题,线程安全问题是只出现在多线程中的一个问题.因为多线程情况下有共享数据,每个线程都共享这些数据并对这 ...
- volatile不能保证线程安全
对于volatile这个关键字,相信很多朋友都听说过,甚至使用过,这个关键字虽然字面上理解起来比较简单,但是要用好起来却不是一件容易的事. 这篇文章将从多个方面来讲解volatile,让你对它更加理解 ...
最新文章
- JS事件:target与currentTarget区别
- 1.15 Java访问控制修饰符(public、 private、protected 和 friendly)
- pageadminCMS.Net Framework的安装教程
- 【渝粤题库】国家开放大学2021春2227物业设备设施管理题目
- C#开发 —— 基础知识
- android如何适配平板,适用于平板电脑、大屏设备和可折叠设备的自适应布局
- 用 普通 用户欺骗登陆 获取 管理员的最高权限~~~~
- 企业应用超级App来啦!
- java中的mapper是什么_Java使用ObjectMapper的简单示例
- python报表自动化系列 - 为pandas.DataFrame制作自然数索引(更改索引为从1开始的自然数)
- C语言的数据类型→字符型数据
- java怎样学_告诉你java如何学
- 你的灯还亮着吗阅读笔记之二
- 一行python代码查找中文同义词(synonyms)
- 医院职工离职申请证明模板,共计10篇
- Vue下载文件不成功及下载文件名称问题
- SQLServer 查询建表语句
- html 字怎么居中怎么写,html文字居中代码怎么写
- 原生js倒计时插件封装
- 总有些中文情歌,让我莫明的感动了
热门文章
- 内网ip和外网ip区别
- 计算机如何在文段中插入符号,插入项目符号与编号(Word 2013基础)——想象力电脑应用...
- nltk.stem 词干提取(stemming)
- 浅析贴片电感的作用及使用原理
- linux桌面小程序开发日记_1(pyqt5 + yolov5)
- excel 中vb组合框_Excel 2013中的工作表组合框问题
- Python名词解释
- java简单小程序输出所有汉字代码实例
- 【计算机网络 (谢希仁) 习题题解】第5章 运输层 (5)——TCP的运输连接管理
- friendly发音_friendly是什么意思