Javajava jvm内存模型型FAQvolatile是干什么用的

更多公众号:gh_ca技术交流,IT技术文章推送。IT热点关注。编程技术交流。最新文章相关作者文章搜狗:感谢您阅读3年工作经验的程序员应该具备的技能,本文由网友投稿产生,如果侵犯了您的相关权益,请联系管理员。Java内存模型与volatile关键字_ImportNew_传送门
Java内存模型与volatile关键字
(点击上方公众号,可快速关注)Java内存模型(Java Memory Model)Java内存模型(JMM),不同于Java运行时数据区,JMM的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中读取数据这样的底层细节。JMM规定了所有的变量都存储在主内存中,但每个线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量,工作内存是线程之间独立的,线程之间变量值的传递均需要通过主内存来完成。volatile关键字平时在阅读jdk源码的时候,经常看到源码中有写变量被volatile关键字修饰,但是却不是十分清除这个关键字到底有什么用处,现在终于弄清楚了,那么我就来讲讲这个volatile到底有什么用吧。当一个变量被定义为volatile之后,就可以保证此变量对所有线程的可见性,即当一个线程修改了此变量的值的时候,变量新的值对于其他线程来说是可以立即得知的。可以理解成:对volatile变量所有的写操作都能立刻被其他线程得知。但是这并不代表基于volatile变量的运算在并发下是安全的,因为volatile只能保证内存可见性,却没有保证对变量操作的原子性。比如下面的代码:/*** 发起20个线程,每个线程对race变量进行10000次自增操作,如果代码能够正确并发,* 则最终race的结果应为200000,但实际的运行结果却小于200000。** @author Colin Wang**/public class VolatileTest {public static volatile int race = 0;public static void increase() {
race++;}private static final int THREADS_COUNT = 20;public static void main(String[] args) {
Thread[] threads = new Thread[THREADS_COUNT];
for (int i = 0; i < THREADS_COUNT; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 10000; i++) {
increase();
threads[i].start();
while (Thread.activeCount() > 1)
Thread.yield();
System.out.println(race);}}这便是因为race++操作不是一个原子操作,导致一些线程对变量race的修改丢失。若要使用volatale变量,一般要符合以下两种场景:变量的运算结果并不依赖于变量的当前值,或能够保证只有单一的线程修改变量的值。变量不需要与其他的状态变量共同参与不变约束。使用volatile变量还可以禁止JIT编译器进行指令重排序优化,这里使用单例模式来举个例子:/*** 单例模式例程一** @author Colin Wang**/public class Singleton_1 {private static Singleton_1 instance =private Singleton_1() {}public static Singleton_1 getInstacne() {
* 这种实现进行了两次instance==null的判断,这便是单例模式的双检锁。
* 第一次检查是说如果对象实例已经被创建了,则直接返回,不需要再进入同步代码。
* 否则就开始同步线程,进入临界区后,进行的第二次检查是说:
* 如果被同步的线程有一个创建了对象实例, 其它的线程就不必再创建实例了。
if (instance == null) {
synchronized (Singleton_1.class) {
if (instance == null) {
* 仍然存在的问题:下面这句代码并不是一个原子操作,JVM在执行这行代码时,会分解成如下的操作:
* 1.给instance分配内存,在栈中分配并初始化为null
* 2.调用Singleton_1的构造函数,生成对象实例,在堆中分配
* 3.把instance指向在堆中分配的对象
* 由于指令重排序优化,执行顺序可能会变成1,3,2,
* 那么当一个线程执行完1,3之后,被另一个线程抢占,
* 这时instance已经不是null了,就会直接返回。
* 然而2还没有执行过,也就是说这个对象实例还没有初始化过。
instance = new Singleton_1();
}}}/*** 单例模式例程二** @author Colin Wang**/public class Singleton_2 {/* * 为了避免JIT编译器对代码的指令重排序优化,可以使用volatile关键字, * 通过这个关键字还可以使该变量不会在多个线程中存在副本, * 变量可以看作是直接从主内存中读取,相当于实现了一个轻量级的锁。 */private volatile static Singleton_2 instance =private Singleton_2() {}public static Singleton_2 getInstacne() {
if (instance == null) {
synchronized (Singleton_2.class) {
if (instance == null) {
instance = new Singleton_2();
}}}变量在有了volatile修饰之后,对变量的修改会有一个内存屏障的保护,使得后面的指令不能被重排序到内存屏障之前的位置。volalite变量的读性能与普通变量类似,但是写性能要低一些,因为它需要插入内存屏障指令来保证处理器不会发生乱序执行。即便如此,大多数场景下volatile的总开销仍然要比锁低,所以volatile的语义能满足需求时候,选择volatile要优于使用锁。出处:ImportNew - 赖信涛链接:/13256.htmlImportNew 专注 Java 技术分享,欢迎关注。微信号:ImportNew(长按上图 ↑↑↑ 可自动识别二维码)
觉得不错,分享给更多人看到
ImportNew 微信二维码
分享这篇文章
6月12日 15:23
ImportNew 最新文章
ImportNew 热门文章让天下没有难学的技术
Java并发中正确使用volatile
Java并发中正确使用volatile
整理和翻译自Twitter实时搜索的
前几天并发编程群里有同学对volatile的用法提出了疑问,刚好我记得Twitter有关实时搜索的这个对这个问题解释的很清晰并有一个实际的应用场景,于是周末把这个问题摘录了一些和并发相关的内容如下:
并发 &#8211; 定义
悲观锁 &#8211; Pressimistic locking
一个线性在执行一个操作时持有对一个资源的独占锁。(互斥)
一般用在冲突比较可能发生的场景下
乐观锁 &#8211; Optimistic locking
尝试采用原子操作,而不需要持有锁;冲突可被检测,如果发生冲突,具有相应的重试逻辑
通常用在冲突较少发生的场景下
非阻塞算法 &#8211; Non-blocking algorithm
算法确保对线程间竞争共享资源时候,不会因为互斥而使任一线程的执行无限延迟;
无锁算法 &#8211; Lock-free algorithm
如果系统整个流程的执行是无阻塞的(系统某一部分可能被短暂阻塞),这种非阻塞算法就是无锁的。
无锁算法比传统的基于锁的算法对系统的开销更小,且更容易在多核多CPU处理器上扩展;
在实时系统中可以避免锁带来的延迟;
CAS (compare and swap)或LL/SC(load linked/store conditional),以及内存屏障相关的指令经常被用在算法实现中。
无等待算法 &#8211; Wait-free algorithm
如果每个线程的执行都是无阻塞的,这种非阻塞算法就是无等待的(比无锁算法更好)
Java的并发
Java的内存模型并不保证一个线程可以一直以程序执行的顺序看到另一个线程对变量的修改,除非两个线程都跨越了同一个内存屏障。(Safe publication)
Java内存模型
代码顺序规则
一个线程内的每个动作 happens-before 同一个线程内在代码顺序上在其后的所有动作
volatile变量规则
对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入
如果A happens-before B, B happens-before C,那 A happens-before C
Safe publication案例
class VolatileExample {
int x = 0;
volatile int b = 0;
private void write() {
private void read() {
int dummy =
while (x != 5) {
public static void main(String[] args) throws Exception {
final VolatileExample example = new VolatileExample();
Thread thread1 = new Thread(new Runnable() {
public void run() {
example.write();
Thread thread2 = new Thread(new Runnable() {
public void run() {
example.read();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
x并不需要定义为volatile, 程序里可以有需要类似x的变量,我们只需要一个volatile变量b来确保线程a能看到线程1对x的修改:
根据代码顺序规则,线程1的x=5; happens-before b=1;; 线程2的int dummy = happens-before while(x!=5);
根据volatile变量规则,线程2的b=1; happens-before int dummy=b;
根据传递性,x=5; happens-before while(x!=5);
在JSR-133之前的旧Java内存模型中,虽然不允许volatile变量之间重排序,但旧的Java内存模型仍然会允许volatile变量与普通变量之间重排序。JSR-133则增强了volatile的内存语义:严格限制编译器(在编译器)和处理器(在运行期)对volatile变量与普通变量的重排序,确保volatile的写-读和监视器的释放-获取一样,具有相同的内存语义。
延伸阅读: ,
原创文章,转载请注明: 转载自本文链接地址:
花名一粟,淘宝资深架构师。
Latest posts by hugozhu ()
Related posts:
(6 votes, average: 2.50 out of 5)
Loading...让天下没有难学的技术
同步和Java内存模型
同步和Java内存模型
作者:Doug Lea 译者:程晓明,萧欢,杜建雄
校对:方腾飞,丁一,欧振聪
原创文章,转载请注明: 转载自本文链接地址:
花名清英,并发网()创始人,畅销书《Java并发编程的艺术》作者,蚂蚁金服技术专家。目前工作于支付宝微贷事业部,关注互联网金融,并发编程和敏捷实践。微信公众号aliqinying。
Latest posts by 方 腾飞 ()
Related posts:
(2 votes, average: 3.00 out of 5)
Loading...

我要回帖

更多关于 java线程内存模型 的文章

 

随机推荐