Java学习笔记(一)volatile与多线程

被问到Volatile,讲不清楚,回来好好补课。

几个相关的知识要理解:多线程,内存模型,乱序优化,锁,原子性,可见性


如果不定义为volatile,会发生什么

主内存中的数据,例如var,会被缓存在寄存器中,进行read/write操作之后,在某个时间复制回内存。在多核CPU上运行多线程的程序,共享变量会因为多个线程,产生多个寄存器中的缓存,在一个线程中的修改,被写回内存之前,其他线程是看不到的。


volatile的效果

一个共享变量var定义为volatile,那么,如果两个线程,a线程write,b线程在之后read,那么前者的操作结果一定会被后者读到,也即:b线程读到的一定是a线程write之后的结果(假设中间没有其他操作)

    ref: http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4

    根据Java语言规范,volatile保证了共享变量在多线程下的“可见性”


volatile如何保证可见性

编译器遇到volatile时,就会要求CPU不在寄存器中进行缓存,而是直接操作内存。

要更好地理解可见性,可以阅读Java语言规范的内存模型一节:http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4


原子性

在Java Tutorial中,对原子操作是这样描述的:

  • Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
  • Reads and writes are atomic for all variables declared volatile (including long and doublevariables)
问题:一个对象,有两个int字段,i、j,要对它们分别加一。将这个对象定义为volatile能保证两个加一操作的一致性吗?答案显然是不能。

所以,第二句话中的all variables,我只能理解为primitive variables


实现复杂操作的原子性

还是离不开锁

两种锁,悲观锁,乐观锁

Synchronize属于悲观锁,用得很多了,不需赘述

乐观锁,在这里一般就指 CAS - Compare and Swap,不加锁,每次write操作之前,检查操作目标的当前状态是否和本线程中缓存的状态一致,如果不一致,则重新读取,重新尝试write操作。

阅读java.util.concurrent.atomic包中的源代码,可以看到大量调用了 sun.misc.Unsafe.compareAndSwapXxx()

Compare和Swap是两个动作,如何保障这两个动作的原子性,也即这两次操作期间没有其他线程修改操作目标?这就需要阅读sun.misc.Unsafe的源码,进行更深入的研究了。根据网上看到的资料,这部分是JNI方法。还没有去找这部分代码来读,以后有机会再继续学习。

下面这个网页是对 sun.misc.Unsafe 的一个介绍:http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

需要说的是,用Volatile和CAS,性能未必比synchronize/Lock好。需要具体情况具体分析。

网上关于应用Volatile的推荐原则如下:

    (1)写入变量不依赖此变量的值,或者只有一个线程修改此变量
    (2)变量的状态不需要与其它变量共同参与不变约束
    (3)访问变量不需要加锁


小结

1. Java volatile 提供操作对象在多线程之间的可见性,volatile变量不会在寄存器中缓存,只保存在内存中

2. volatile本身不提供原子性,但可以结合 CAS 的方式,实现非阻塞的并行计算

3. 因为Java规范保证对原始数据类型操作的原子性,因此,对此类数据的访问,可以利用volatile实现不加锁的同步访问,但其性能未必是最优的


扩展阅读:

JSR 166 Concurrency Utilities

defungo的博客:深入浅出 Java Concurrency (2): 原子操作

Hsuxu的专栏 - JAVA CA原理深度分析

剖析为什么在多核多线程程序中要慎用volatile关键字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值