相信大多数Java程序员都学习过volatile这个关键字的用法。百度百科上对volatile的定义:java
volatile是一个类型修饰符(type specifier),被设计用来修饰被不一样线程访问和修改的变量。volatile的做用是做为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。程序员
可能有不少刚学Java的朋友们看了上面这段很是笼统的描述后仍然以为云里雾里的。ide
下面咱们就用一个具体的例子来学习volatile的用法。学习
看这个例子:优化
public class ThreadVerify { public static Boolean stop = false; public static void main(String args[]) throws InterruptedException { Thread testThread = new Thread(){ @Override public void run(){ int i = 1; while(!stop){ //System.out.println("in thread: " + Thread.currentThread() + " i: " + i); i++; } System.out.println("Thread stop i="+ i); } } ; testThread.start(); Thread.sleep(1000); stop = true; System.out.println("now, in main thread stop is: " + stop); testThread.join(); } }
这段代码在主线程的第二行定义了一个布尔变量stop, 而后主线程启动一个新线程,在线程里不停得增长计数器i的值,直到主线程的布尔变量stop被主线程置为true才结束循环。操作系统
主线程用Thread.sleep停顿1秒后将布尔值stop置为true。线程
所以,咱们指望的结果是,上述Java代码执行1秒钟后中止,而且打印出1秒钟内计数器i的实际值。设计
然而,执行这个Java应用后,你发现它进入了死循环,在任务管理器里发现这个Java程序CPU占用率飙升。code
缘由是什么呢?让咱们温习下计算机专业课操做系统中讲过的内存模型的知识。内存
以Java内存模型为例,Java内存模型分为主内存(main memory)和工做内存(work memory)。主内存内的变量由全部线程共享,每一个线程拥有本身的工做内存,里面的变量包含了线程局部变量。主内存中的变量若是被线程使用到,则线程的工做内存会维护一份主内存变量的副本拷贝。
线程对变量的全部读写操做必须在工做内存中进行,不能直接操做主内存中的变量。不一样线程之间也没法直接访问对方的工做内存。线程间变量的传递需经过主内存来完成。线程、主内存、工做内存三者之间的交互关系以下图:
若是线程在本身的执行代码里修改了定义在主线程(主内存)中的变量,修改直接发生在线程的工做内存里,而后在某个时刻(Java程序员没法控制这个时刻,而是由JVM调度的),这个修改从工做内存写回到主内存。
回到咱们的例子。尽管主线程修改了stop变量,可是仅仅修改了主内存中的值,而操做计数器的线程的工做内存里的stop变量仍是旧的值,始终为false。所以这个线程陷入了死循环。
知道了原理,解决方案就很简单了。在stop变量前加上关键字volatile进行修饰,这样在计数器线程里每次读取stop的值时,volatile会强制该线程从主内存读取,而不是从当前线程的工做内存读取。这样就避免了死循环。下图显示1秒钟以后,计数器执行了14亿次。
要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码: