volatile概念
是java虚拟机提供的轻量级的同步机制
特性
保证可见性 |
说到volatile的可见性就要先说说JMM模型
JMM内存模型
JMM(Java内存模型)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中的各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。
JMM关于同步的规定:
- 线程解锁前,必须把共享变量的值刷回主内存
- 线程加锁前,必须读内存的最新值到自己的工作内存
- 加锁解锁是同一把锁
JMM特性
- 可见性
- 原子性
- 有序性
验证volatile可见性
资源类
class MyData{
int number=0;
public void addTo60(){
this.number=60;
}
}
main方法
public static void main(String[] args){
MyData myData=new MyData();
new Thread(()-> {
System.out.println(Thread.currentThread().getName()+"\t come in");
// 暂停线程
try { TimeUnit.SECONDS.sleep(3);}catch (InterruptedException e){e.printStackTrace();}
myData.addTo60();
System.out.println((Thread.currentThread().getName()+"\t updated number value:" +myData.number));
},"A").start();
// 第2个线程就是我们的main线程
while(myData.number==0){
}
System.out.println(myData.number);
System.out.println(Thread.currentThread().getName()+"\t mission is over main get number:"+myData.number);
}
执行结果
代码停留在 while(myData.number==0){ },一直没有动
分析:
这是因为,在A线程中虽然修改了number=60,但是由于主内存修改后没有办法通知线程main,所以number此时仍为0
优化
结果
分析:
明显,main线程收到了A线程修改变量的通知,结果被打印出来
不保证原子性 |
原子性:不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分隔。需要整体完整,要么同时成功,要么同时失败。
验证volatile不保证原子性
资源类
class MyData{
volatile int number=0;
public void addTo60(){
this.number=60;
}
public void addPlus(){
this.number++;
}
}
main
public static void main(String[] args){
MyData myData=new MyData();
for (int i = 0; i <=20 ; i++) {
new Thread(()->{
for (int j=1;j<=1000;j++){
myData.addPlus();
}
},String.valueOf(i)).start();
}
// 需要等待20个线程全部计算完成,再用main线程取得最后结果值
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"\t final number value"+myData.number);
}
执行结果
禁止指令重排 |
为了优化程序性能,编译器和处理器会对java编译后的字节码和机器指令进行重排序