volatile关键字
一、高并发中的可见性,有序性,原子性
二、代码分析volatile关键字
package com.lyb.jmm; /** * @ClassName VolatileTest * @Description * @Author Lyb * @Date 2019/12/4 15:48 * @Version V1.0 **/ public class VolatileTest { private static volatile int num=0; public static void count() { num++; } public static void main(String[] args) throws Exception { Thread[] ts=new Thread[10]; //线程数组中加10个线程,都对num做1000次加 for (int i=0;i<10;i++){ ts[i]=new Thread(new Runnable() { @Override public void run() { for(int i=0;i<1000;i++){ count(); } } }); ts[i].start(); } //join线程 for (Thread t :ts){ t.join(); } //输出结果 System.out.println(num); } }
输出结果为: 多次执行发现结果都是小于等于10000的
分析:
如图,因为volatile是会在num改变的时候将信息告知给cpu总线嗅探监听。这个时候线程2发现我的工作内存中也有num这个变量,然后这个变量就失效了。但是如果说T1和T2都对num做了一次++,num在他们的内存中都是1,但是T2中的失效了,就会重新从主内存read并load变量num得到的也是1,随着循环的继续,num++在T2中就被assign成了2但是,此时num++已经执行了三次了,T1,T2中分别++一次,T2读到变量值后再++一次,但是结果却是3。这就是因为volatile关键字没有原子性。再store的时候,变量失效了就不会被store并write到内存中,会少掉对变量操作的记录,这一次num++不具备原子性。如果想要程序具有原子性,要对代码块加sychronized关键字了
有序性:
volatile与有序性
多线程通过抢占时间片来执行自己的代码体,所以我们会感觉到线程是同时执行完的,除了引入了时间片以外,由于处理器优化和指令重排等,CPU还可能对输入代码进行乱序执行,比如我们拿到数据要执行写库,查询,删除这三个操作,这就会可能要涉及到有序性的问题了。
volatile可以禁止指令重排,这就保证了代码的程序会严格按照代码的先后顺序执行。这就保证了有序性。被volatile修饰的变量的操作,会严格按照代码顺序执行接下来我们就说一下为了实现volatile内存语义JMM是怎样限制重排序(包括编译器重排序和处理器重排序)的。
走自己的路