首页 > 代码库 > volatile关键字及内存可见性
volatile关键字及内存可见性
先看一段代码:
package com.java.juc; public class TestVolatile { public static void main(String[] args) { ThreadDemo td = new ThreadDemo(); new Thread(td).start(); while(true){ if(td.isFlag()){ System.out.println("----------------"); break; } } } } class ThreadDemo implements Runnable{ private boolean flag = false; public void run() { try { Thread.sleep(200); flag = true; System.out.println("flag= "+ isFlag()); } catch (InterruptedException e) { e.printStackTrace(); } } /** * @return the flag */ public boolean isFlag() { return flag; } /** * @param flag the flag to set */ public void setFlag(boolean flag) { this.flag = flag; } }
运行这段代码,运行的结果只有:
flag= true
这是由于 子线程在操作共享数据时,会将主存总的flag,复制一份到线程的缓存中进行操作,操作完成后会再将数据写到主存中,由于while(true)是一个运行效率非常高的一句代码,而且运行速度非常快,导致主线程再执行循环时没有机会从主存中读取到数据,导致flag的值是主线程最初读到的值,flag = false;
在共享变量上加一个volatile关键字,会解决这个问题
package com.java.juc; public class TestVolatile { public static void main(String[] args) { ThreadDemo td = new ThreadDemo(); new Thread(td).start(); while(true){ if(td.isFlag()){ System.out.println("----------------"); break; } } } } class ThreadDemo implements Runnable{ private volatile boolean flag = false; public void run() { try { Thread.sleep(200); flag = true; System.out.println("flag= "+ isFlag()); } catch (InterruptedException e) { e.printStackTrace(); } } /** * @return the flag */ public boolean isFlag() { return flag; } /** * @param flag the flag to set */ public void setFlag(boolean flag) { this.flag = flag; } }
----------------
flag= true
volatile 关键字的作用:当多个线程操作共享数据时,可以保证内存中的数据是可见的。
可以使用内存再来解释,可以认为各个线程操作各自独立缓存中的数据是实时与主存进行同步的,可以理解为多个线程直接在主存中操作数据,而不是在各自的缓存中。
加了volatile后性能还是比什么都不加要讲的,但是比锁的效率要高。
加了volatile之后效率会低在哪?
实际上JVM底层有个优化,叫指令重排序,使用volatile修饰后就不能重排序了。
加锁的效率是最低的(使用synchronized关键字),同步锁会使线程每次从主存中读取数据,但是同步锁的效率很低。有多个线程来访问时先判断锁,如果被其他线程锁挡住,则线程会进入阻塞挂起状态,那得等到下次cpu再次分配任务,才能执行。
当然:volatile和synchronized还是不同的。volatile相较与synchronized是一种较为轻量级的同步策略。
注意:
1. volatile 不具备 "互斥性"
2. volatile 不能保证变量的 "原子性"
volatile关键字及内存可见性