文章:点击打开链接
lock有两种: 一种是共享资源互斥,一种是可见性(visibility)
1》互斥:是由syn的对象锁和wait(),notify()合作,lock锁来保证
visibility:就是由volatile变量+原子操作----代替syn对象锁(主要由变量之间不能改变顺序来决定)。
visibility:1》一个线程改变一个值后,另一个线程可以马上可以看见新改值。
2》happens-before:一定保证指令的原有顺序,不要打乱。
一个线程保存volatile变量之前,必须可以看见排在它前面变量值:表示volatile变量之前的语句不能打乱顺序,然后volatile变量存入内存。
一个线程读volatile变量后,得出新值,哪么排在它前面的变量的值也可以看见:表示保存时volatile变量同时保存volatile变量前的变量,然后读可以读到新值。
一、volatile 解决cpu缓存和内存数据不相同,从主存读,写到主存。
线程操作:先写后读顺序
1、两个线程运行在不同cpu中
1>thread1 运行在cpu1中,读时从cpu1 cache 读,有就取出,没有去主存读,并且复制一份到缓存中。
写时写入到cpu1 cache 中,再写入到主存,但是什么时候写入到主存,不知道。
2>thread2 运行在cpu2中,读时从cpu2 cache 读,有就取出,没有去主存读,并且复制一份到缓存中。
写时写入到cpu2 cache 中,再写入到主存 。
2、一个线程写,多个线程读,共享变量public int counter=0.
第一次初始化时,内存默认保存counter=0。
线程1改了数据counter=7,放入cpu1 cache中,但是并没有存入内存。
线程2首先到cpu2 cache中读,没有就去主存读,取得counter=0,并复制到cpu2 cache中。
**结果线程1的cpu1 cache counter=7,主存 counter=0 数据不一致。
这样两个线程:线程1已经改变了值,前面的人明明改变共享数据值,线程2却得不到最新的值,问题了出来了?
解决:public volatile int counter=0,这时 threa1 写到 cpu1 cache 后,马上写到主存
读时 cpu2 cache无效,直接从主存读。
二、两个线程去内存取数据时,进行加1,哪么线程1和线程2的cache全部是1.
出错了,正确应该是tread2在thread1写入内存之后去内存取数,得出2正确值。
理由: 因为你是用volatile变量去维持原子操作,即无锁操作,所以两个线程可以同时执行,交叉执行,这样问题来了。
时间上,thread1直接从主存读数据(jvm禁止读时从缓冲读,但是取数后放哪里不管)couter=0放入缓冲区,thread1数据还没有保存到内存,thread2取了一个数据counter=0的值,也进行改,所以thred2取数出先问题,所以只能用syn对象锁,规定操作时单一执行。
三、 volatile 变量一大特点:就是可以把它前面的所有non-volatile变量一起保存。
指令排序问题,即在volatile 变量前的变量不能移动volatile 变量后面,
有的说编译器,有的说jvm会打乱指令的顺序,也就是重排,不知道到底是谁啊,管它的?
happens-before:一条指令的执行一定在另一条指令之前执行,也就是保证顺序性。
当重排后,CPU执行指令会乱序或并行运行,只有上面的happens-before所规定的情况下,才保证顺序性。
1》例如:有两个线程,thread1时不时去保存新对象,thread2:时不时去取新对象,由于没有设置syn对象锁,所以它俩可以同时执行,一前一后交叉执行。
当线程1保存 volatile hasNewObject值,它之前的object非变量也一起保存到主存:
所以:当volatile前面有多个变量时,只需定义最后一个变量为volatile hasNewObject变量。
下面put,take可以不用syn对象锁,也可以执行很好,即无锁操作,volatile变量+原子操作。
public class Exchanger {
private Object object = null;
private volatile hasNewObject = false;
public void put(Object newObject) {
while(hasNewObject) {
//wait - do not overwrite existing new object
}
object = newObject;
hasNewObject = true; //volatile write
}
public Object take(){
while(!hasNewObject){ //volatile read
//wait - don't take old object (or null)
}
Object obj = object;
hasNewObject = false; //volatile write
return obj;
}
}
2》错误,如果jvm重排以上指令,hasNewobject 排在object之前,哪么指令顺序出错问题来了,
线程1写了hasNewobject后,这时线程2刚好看见hasNewobject =true,以为有新对象了,人家还没给新对象呢,所以取了空值。
while(hasNewObject) {
//wait - do not overwrite existing new object
}
hasNewObject = true; //volatile write
object = newObject;
四、总结
volatile variables 从主存直接读、写,比从cache读浪费更多的性能(cpu,主存),但是阻止指令重新排序提高了计算机性能。
所以当我们用syn对象锁时,变量可见性有问题时,就用volatile 变量来维持原子操作,所以volatile也算是syn对象锁的一点小补充。