java 线程Thread 技术--volatile关键字

时间:2022-01-13 18:30:19

java 语言中允许线程访问共享变量,为了保证共享变量能被准确和一致的更新,Java 语言提供了volatile 关键字,也就是我们所说的内存一致性

问题抛出:(尝试去运行下面代码,以及将volatile 加上的运行结果)

/**
* 问题抛出
* @param args
*/
public class Volatile01 { public /*volatile*/ boolean flag = true; public static void main(String[] args) throws InterruptedException {
Volatile01 v =new Volatile01();
RunV r =new RunV(v);
Thread th =new Thread(r);
th.start(); //在main method to modify flag
Thread.sleep(1000);
v.flag=false;
} } class RunV implements Runnable{
Volatile01 vola ;
RunV(Volatile01 vola){
this.vola=vola;
} @Override
public void run() {
//
while(vola.flag) {} System.err.println("flag 状态转变"); } }

问题的原因是是什么呢?

根据上面的运行结果,在没有volatile 关键字的情况下,运行结果是:线程持续陷入死循环状态,flag 并没有改变;

加上volatile 后 运行结果:flag 状态转变,说明flage 已经被改变了;

请看:http://www.infoq.com/cn/articles/ftf-java-volatile 这篇文章,我大致的说一下:主要跟处理器有关,处理器为了运行的效率,不直接跟内存进行通讯;

它是先将内存的数据先读取到cpu 内部缓存(L1,L2,其他)中,操作完之后不知道什么时候才会将数据重新写到内存来修改内存中存在的值,所以在上面的例子中

在修改了flage =true 后,RunV线程是无法感知到的,依然是cpu 缓存的值;

volatile如何保证可见性呢?

依然看上文的那篇文章;里面由一些图文解释;

volatile保证原子性操作吗?是否能解决数据不一致的问题(尝试多运行如下代码,可以得到都不同的结果)?

/**
* volaile 是否支持原子性操作
* @param args
*/
public class Volatile02 { public volatile int v=0; public static void main(String[] args) throws InterruptedException, ExecutionException {
Volatile02 v =new Volatile02();
RunB r =new RunB(v);
List<Thread> theads =new ArrayList<Thread>();
for(int i=0;i<10;i++) {
theads.add(new Thread(r));
} theads.forEach((t->{
t.start();
})); theads.forEach((t->{
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
})); System.out.println(v.v);
} } class RunB implements Runnable{
Volatile02 vola ;
RunB(Volatile02 vola){
this.vola=vola;
} @Override
public void run() {
for(int i=0;i<10000;i++) { vola.v++;
System.out.println(Thread.currentThread().getName());
} } }

运行得知,volatile 尽管保证了内存的可见性,但并不能保证多线程下的原子操作导致数据不一致问题;只能靠synchronized 和lock;