概述
volatile是java中的一个关键字,主要什么作用呢?
- 把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会对该变量上的操作与其他内存操作仪器重排序。
- volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量总是会返回最新的写入数据
volatile工作机制
java内存模型(图片出处:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html):
volatile 修饰的变量read的时候直接从内存中加载,而且store的时候即为write,即内存可见性。单纯的读->写操作,可以理解为写入volatile变量相当于退出同步代码块,而读取volatile变量就相当于进入同步代码块。
下面主要来看一段代码:
import java.util.*;
/**
* Created by sanyinchen on 15/12/4.
*/
public class volatileMain {
static volatile int i = 0;
private static void increase() {
i++;
}
public static void main(String[] arges) {
int max=10000;
Thread[] threadList = new Thread[max];
for (int number = 0; number < max; number++) {
Thread t=new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
increase();
}
};
threadList[number]=t;
}
for (int number = 0; number < max; number++) {
threadList[number].start();
}
for (int number = 0; number < max; number++) {
if(threadList[number].isAlive()){
try {
threadList[number].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("activeCount : " + Thread.activeCount());
ThreadGroup group=Thread.currentThread().getThreadGroup();
Thread[] sidList=new Thread[group.activeCount()];
group.enumerate(sidList);
for(int j=0;j<sidList.length;j++){
System.out.println(j+" thread name -- "+sidList[j].getName());
}
System.out.println("i : " + i);
}
}
输出结果:
activeCount : 2
0 thread name -- main
1 thread name -- Monitor Ctrl-Break
i : 9810
先来解释一下这段代码:
首先new出10000个线程,遍历循环start(),runable中执行increase()方法,将成员变量i++.
遍历循环线程池,将没有执行完的线程调用join()方法,这里的join()的意思是阻塞当前的线程,也就是main()线程,等待其join的线程执行完毕。这里主要是保证所开的线程全部执行完成。最后输出结果和当前存存活的线程,来检验程序的运行结果是否有说服力。
那么最后的结果为何是<1000的呢?volatile是一个轻量级的同步机制,write和read确实是原子操作,那么它的问题出在i++,i=i+1需要经过:
1. 读取i
2. 对i进行操作
3. 将i写入主内存
即时1与3是原子的,但是任然不能保证对i的操作是原子的,这就是导致问题的所在。
volatile的应用
volatile最广泛的应用时作为一个标记
我们会经常这么写:boolean asleep; while(!asleep){ // do somthing }
这里若是单线程不会有什么问题,问题是如果是多线程,asleep在经过每个线程的内存空间的时候就可能会导致并发导致最终写入主内存的asleep是true。那么就将导致死循环.
正确的写法是:volatile boolean asleep; while(!asleep){ // do somthing }
另外再提一下就是在单例模式初始化延迟加载double check中的适用,这里大家有兴趣的话可以去查阅一些相关资料
转载请注明出处:
http://blog.csdn.net/sanyinchen/article/details/50901580