今天聊volatile关键字,定义:volatile修饰的实例变量(instance variable---类加载阶段将symbolic reference转为district reference 并保存到 method area)在线程内部每次使用都强制从工作内存中读取值。
edited at 2019 03.12 23:37---------------------------------------------------------------------------------------------------------------------------
volatile 变量在写操作的时候执行了lock指令,而lock指令所作的三件事情:
1、将volatile变量(就用countNumber)从工作内存保存到主内存中,即从frame中刷到了heap中,
2、将其他用到这个变量的地方的缓存清除(缓存一致性)
3、禁止指令重排序
参考:
此文 的volatile部分
《java并发编程的艺术》2.1节 volatile的应用
---------------------------------------------------------------------------------------------------------------------------------------------------------------
class Thread1 extends Thread{
volatile public static boolean flag=true;
public void setFlag(boolean flag){
this.flag = flag;
}
public void run(){
print("start thread");
while(flag){
}
print("flag is false now,end thread");
}
}
class demo{
main{
Thread1 t = new Thread1();--------------------------1
t.start();
t.setFlag(false);-----------------------------------3
print("main set flag false");
}
}
步骤1:在heap中保存一个Thread1的实例,t(t保存在vm stack)保存该实例heap中首地址。
执行t.start() (对java中线程实现还没深入),从vm stack 分配内存给执行方法(一般方法执行的stack分配多大已经在编译时期就确定了),这里分配的叫frame,frame中保存了local variable,所属方法在run-time constant pool中的引用,操作栈(类似CPU中的ALU,负责运算),返回地址。那么这里的私有内存就是在操作栈(operand stack),也就是线程私有stack。如果不加volatile,那么运行的flag永远不会被读取到false,即使main线程已经将instance variable设置成了false(步骤3),加了volatile,就是强制在判断执行前,插入一个指令,从heap中读取flag值并更新到operand stack中。这就是volatile的可见性。但是并不能保证原子性。也就是不能保证同步。这里更正一下,对volatile修饰的变量的单个操作满足原子性,但是复合操作不满足原子性。可以参看此文,原子性的保证通过两种方式:
1:确定可以修改该实例变量的方法有几个。对这几个方法进行class锁,不是instance 锁。
2:如果是很多途径可以修改这个变量,那就要原子类。
附上图片易于理解
自己的理解。有不对的地方请指正。
参考:
《java多线程编程核心技术》(第二章volatile部分)---高洪岩
《深入理解java虚拟机》---周志明
《java concurrency in practice》---Brian Goetz