Java学习笔记(一)volatile与多线程
2014-05-09 12:57
344 查看
被问到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
Reads and writes are atomic for all variables declared
问题:一个对象,有两个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关键字
几个相关的知识要理解:多线程,内存模型,乱序优化,锁,原子性,可见性
如果不定义为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
longand
double).
Reads and writes are atomic for all variables declared
volatile(including
longand
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关键字
相关文章推荐
- java多线程学习笔记——volatile关键字
- java学习笔记1017---多线程
- java学习笔记14--多线程编程基础1
- Java学习笔记之多线程
- Java多线程学习笔记2——ThreadLocal
- [零散篇]Java学习笔记---Java的Socket网络编程以及多线程
- Bravo!Java学习笔记(3)---多线程
- Java多线程学习笔记—线程停止
- Java多线程学习笔记
- [原]Java多线程编程学习笔记之一:线程中断(含代码)
- java多线程学习笔记2---理解原子性和可见性
- java学习笔记10——多线程的学习
- JAVA 多线程和并发学习笔记(四)
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
- 学习笔记 Java_多线程 2014.7.17
- 学习笔记 java 多线程(二)
- [学习笔记]Java多线程经典问题
- java 多线程学习笔记2-同步代码块,死锁
- Java学习笔记―第十一章 多线程机制
- java多线程学习笔记