volatile关键字有两个特性
-
内存可见性
java内存模型规定对于多个线程共享的内存变量,是存储在主内存当中。每个线程都有自己独立的工作内存,并且它们只能访问自己的工作内存,其他线程的内存对于它是不可见的。工作中的内存保存了主内存中的共享变量的副本,线程想要操作这些主内存当中共享的变量,只能通过操作自己工作内存中的副本,然后再同步到主内存来实现。
内存操作模型如下所示:
- 局部阻止重排序
重排序的目的是提高运行并发度,发生在编译器和处理器阶段,遵循as-if-serial语义(不管怎么重排序,单线程程序的执行结果不能改变),也就是重排序所带来的问题是针对多线程的。
重排序发生的条件是A和B没有存在依赖关系,这里的依赖关系是指数据依赖关系和控制依赖关系两种。
如下示例:
private int a;
private int b;
在线程A中有两条语句对这两个共享变量进行赋值操作:
a = 1;
b = 2;
假设当线程A对a进行复制操作的时候发现这个变量在主内存已经被其它的线程加了访问锁,那么此时线程A怎么办?等待释放锁?不,等待太浪费时间了,它会去尝试进行b的赋值操作,b这时候没被人占用,因此就会先为b赋值,再去为a赋值,那么执行的顺序就变成了:
b = 2;
a = 1;
而Volatile关键字就能阻止这种指令重新排序,确保并发时程序执行顺序的正确性。
关键字Volatitle是java提供的一种轻量级的同步机制,当一个变量被Volatile修饰之后,它有以下特殊的访问规则如下
线程可见性,当Volatile修饰的变量被某个线程修改之后,无论该变量是否加锁,其它的线程都可以立即看到该变量的最新值,普通变量是不行的。
如下图示例
public class VolatileTest {
public volatile boolean stop=false;
public String out="step one...........";
public void run() throws InterruptedException {
Thread thread1= new Thread(new Runnable() {
@Override public void run() {
while(!stop){
System.out.println(Thread.currentThread().getName()+out);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2= new Thread(new Runnable() {
@Override public void run() {
while(!stop){
System.out.println(Thread.currentThread().getName()+out);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread1.start();
thread2.start();
}
public static void main(String[] args) throws InterruptedException {
VolatileTest test= new VolatileTest();
test.run();
TimeUnit.SECONDS.sleep(2);
test.stop=true;
}
}
运行结果:
Thread-1step one...........
Thread-0step one...........
Thread-1step one...........
Thread-0step one...........
总结:Volatile适用于一个线程写,多个线程读的情况,某些场景用它替换Synchronized并发访问会大大提高。
网友评论