volatile是Java提供的一种轻量级的同步机制,它能够保证变量在多线程环境下的可见性(所谓可见性,是指当一条线程修改了共享变量的值,新值对于其余线程来讲是能够当即得知的)。java
为了更好说明可见性,咱们直接写个例子来测试一下:程序员
public class VolatileTest { //public volatile static int num = 0 ; public static int num = 0 ; public static void main(String[] args) throws InterruptedException { final CountDownLatch cdl = new CountDownLatch(1) ; new Thread(new Runnable() { public void run() { long time = System.currentTimeMillis() ; while (num==0){ } System.out.println("结束:"+(System.currentTimeMillis()-time)); cdl.countDown(); } }).start(); System.out.println("开始运行......."); Thread.sleep(1000); num++ ; System.out.println("修改变量num=="+num); cdl.await(); System.out.println("end"); } }
结果:缓存
按正常理解应该1秒后会打印end,可是实际状况是主线程一直在等待。为何出现这种状况呢,咱们须要先了解一下JMM(java内存模型)多线程
以Java内存模型为例,Java内存模型分为主内存(main memory)和工做内存(work memory)。主内存内的变量由全部线程共享,每一个线程拥有本身的工做内存,里面的变量包含了线程局部变量。主内存中的变量若是被线程使用到,则线程的工做内存会维护一份主内存变量的副本拷贝。测试
线程对变量的全部读写操做必须在工做内存中进行,不能直接操做主内存中的变量。不一样线程之间也没法直接访问对方的工做内存。线程间变量的传递需经过主内存来完成。线程、主内存、工做内存三者之间的交互关系以下图:spa
若是线程在本身的执行代码里修改了定义在主线程(主内存)中的变量,修改直接发生在线程的工做内存里,而后在某个时刻(Java程序员没法控制这个时刻,而是由JVM调度的),这个修改从工做内存写回到主内存。线程
所以咱们可知,虽然咱们在主线程中修改了变量可是子线程由于读的是本身工做内存的副本因此num仍是为0,致使一直死循环。3d
上面的例子只要咱们把num使用volatile修饰便可保存变量在子线程中可见blog
public volatile static int num = 0 ;
所以咱们能够得出一个结论:内存
volatile具有两种特性,第一就是保证共享变量对全部线程的可见性。将一个共享变量声明为volatile后,会有如下效应:
1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的变量强制刷新到主内存中去;
2.这个写会操做会致使其余线程中的缓存无效。
*volatile只能保存变量的可见性,并不能保证原子性。所以并不能彻底替代synchronized,这个在开发的时候须要特别注意。主要缘由是变量的赋值过程主要包含如下3步
1.读取
2.加一
3.赋值
因此,在多线程环境下,有可能线程A将变量读取到本地内存中,此时其余线程可能已经将变量改变,线程A依然对过时的变量进行自加,从新写到主存中,最终致使告终果不合预期
最后发现个疑问:
public class VolatileTest { //public volatile static int num = 0 ; public static int num = 0 ; public static void main(String[] args) throws InterruptedException { final CountDownLatch cdl = new CountDownLatch(1) ; new Thread(new Runnable() { public void run() { long time = System.currentTimeMillis() ; while (num==0){ System.out.println("11111"); } System.out.println("1结束:"+(System.currentTimeMillis()-time)); cdl.countDown(); } }).start(); Thread.sleep(1000); num++ ; cdl.await(); System.out.println("end"); } }
结果:
若是在循环里面System.out.println 一下即便变量num没有被volatile修饰也能够退出循环,目前怀疑子线程中只要调用主线的方法均会从新同步变量。但愿各们大神们能有更专业的解答