网站建设的基本步骤是,网站建设设计风格如何与色彩搭配,网站制作潍坊,网站备案 前置审批号Volatile [ˈvɑːlətl]#xff0c;中文解释#xff1a;反复无常的#xff0c;易变的#xff0c;不稳定的。volatile的本意是告诉编译器#xff0c;此变量的值是易变的#xff0c;每次读写该变量的值时务必从该变量的内存地址中读取或写入#xff0c;不能为了效率使用对…Volatile [ˈvɑːlətl]中文解释反复无常的易变的不稳定的。volatile的本意是告诉编译器此变量的值是易变的每次读写该变量的值时务必从该变量的内存地址中读取或写入不能为了效率使用对一个“临时”变量的读写来代替对该变量的直接读写。编译器看到了volatile关键字就一定会生成内存访问指令每次读写该变量就一定会执行内存访问指令直接读写该变量。若是没有volatile关键字编译器为了效率只会在循环开始前使用读内存指令将该变量读到寄存器中之后在循环内都是用寄存器访问指令来操作这个“临时”变量在循环结束后再使用内存写指令将这个寄存器中的“临时”变量写回内存。在这个过程中如果内存中的这个变量被别的因素(其他线程、中断函数、信号处理函数、DMA控制器、其他硬件设备)所改变了就产生数据不一致的问题。volatile关键字在用C语言编写嵌入式软件里面用得很多不使用volatile关键字的代码比使用volatile关键字的代码效率要高一些但就无法保证数据的一致性。在Java中volatile 会确保我们对于这个变量的读取和写入都一定会同步到主内存里而不是从 Cache 里面读取。Case 1public class VolatileTest {private static volatile int COUNTER 0;public static void main(String[] args) {new ChangeListener().start();new ChangeMaker().start();}static class ChangeListener extends Thread {Overridepublic void run() {int threadValue COUNTER;while ( threadValue 5){if( threadValue! COUNTER){System.out.println(Got Change for COUNTER : COUNTER );threadValue COUNTER;}}}}static class ChangeMaker extends Thread{Overridepublic void run() {int threadValue COUNTER;while (COUNTER 5){System.out.println(Incrementing COUNTER to : (threadValue1) );COUNTER threadValue;try {Thread.sleep(500);} catch (InterruptedException e) { e.printStackTrace(); }}}}}在这个程序里我们先定义了一个 volatile 的 int 类型的变量COUNTER。然后我们分别启动了两个单独的线程一个线程我们叫 ChangeListener。ChangeListener 这个线程运行的任务很简单。它先取到 COUNTER 当前的值然后一直监听着这个 COUNTER 的值。一旦 COUNTER 的值发生了变化就把新的值通过 println 打印出来。直到 COUNTER 的值达到 5 为止。这个监听的过程通过一个永不停歇的 while 循环的忙等待来实现。另外一个 ChangeMaker 线程运行的任务同样很简单。它同样是取到 COUNTER 的值在 COUNTER 小于 5 的时候每隔 500 毫秒就让 COUNTER 自增 1。在自增之前通过 println 方法把自增后的值打印出来。最后在 main 函数里我们分别启动这两个线程来看一看这个程序的执行情况。Incrementing COUNTER to : 1Got Change for COUNTER : 1Incrementing COUNTER to : 2Got Change for COUNTER : 2Incrementing COUNTER to : 3Got Change for COUNTER : 3Incrementing COUNTER to : 4Got Change for COUNTER : 4Incrementing COUNTER to : 5Got Change for COUNTER : 5程序的输出结果并不让人意外。ChangeMaker 函数会一次一次将 COUNTER 从 0 增加到 5。因为这个自增是每 500 毫秒一次而 ChangeListener 去监听 COUNTER 是忙等待的所以每一次自增都会被 ChangeListener 监听到然后对应的结果就会被打印出来。终极原因是volatile保证所有数据的读和写都来自主内存。ChangeMaker 和 ChangeListener 之间获取到的 COUNTER 值就是一样的。Case 2如果我们把上面的程序小小地修改一行代码把我们定义 COUNTER 这个变量的时候设置的 volatile 关键字给去掉重新运行后发现Incrementing COUNTER to : 1Incrementing COUNTER to : 2Incrementing COUNTER to : 3Incrementing COUNTER to : 4Incrementing COUNTER to : 5这个时候ChangeListener 又是一个忙等待的循环它尝试不停地获取 COUNTER 的值这样就会从当前线程的“Cache”里面获取。于是这个线程就没有时间从主内存里面同步更新后的 COUNTER 值。这样它就一直卡死在 COUNTER0 的死循环上了。Case 3再对程序做一个小的修改。我们不再让 ChangeListener 进行完全的忙等待而是在 while 循环里面Sleep 5 毫秒看看会发生什么情况。static class ChangeListener extends Thread {Overridepublic void run() {int threadValue COUNTER;while ( threadValue 5){if( threadValue! COUNTER){System.out.println(Sleep 5ms, Got Change for COUNTER : COUNTER );threadValue COUNTER;}try {Thread.sleep(5);} catch (InterruptedException e) { e.printStackTrace(); }}}}运行结果Incrementing COUNTER to : 1Sleep 5ms, Got Change for COUNTER : 1Incrementing COUNTER to : 2Sleep 5ms, Got Change for COUNTER : 2Incrementing COUNTER to : 3Sleep 5ms, Got Change for COUNTER : 3Incrementing COUNTER to : 4Sleep 5ms, Got Change for COUNTER : 4Incrementing COUNTER to : 5Sleep 5ms, Got Change for COUNTER : 5解释 虽然没有使用 volatile 关键字强制保证数据的一致性但是短短 5ms 的 Thead.Sleep 的线程会让出CPU线程被唤醒后才会去重新加载变量。它也就有机会把最新的数据从主内存同步到自己的高速缓存里面了。于是ChangeListener 在下一次查看 COUNTER 值的时候就能看到 ChangeMaker 造成的变化了。虽然 JMM 只是 Java 虚拟机这个进程级虚拟机里的一个隔离了硬件实现的虚拟机内的抽象模型但是这个内存模型和计算机组成里的 CPU、高速缓存和主内存组合在一起的硬件体系非常相似。以上就是一个很好的“缓存同步”问题的示例。也就是说如果我们的数据在不同的线程或者 CPU 核里面去更新因为不同的线程或 CPU 核有着各自的缓存很有可能在 A 线程的更新因为数据暂时不一致在 B 线程里面是不可见的。参考如果不介意欢迎使用我的分享链接让我赚几个G币