什么是volatile关键字
volatile关键字的作用:保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。
例如以下程序:
public class Test { public static void main(String[] args) { DemoThread thread = new DemoThread(); new Thread(thread).start(); while (true) { if (thread.isFlag()) { System.out.println("---------------"); break; } } } } class DemoThread implements Runnable { private boolean flag = false; @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } setFlag(true); System.out.println("flag is " + isFlag()); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
这段程序,会不会打印出横线呢?显然是不会的,为什么呢?这个时候就涉及到了内存可见性。
内存可见性
实际上,当我们的程序运行时,JVM会为每一个执行任务的线程分配一个独立的缓存以提高其运行效率。所以当两个及以上的线程操作共享数据时,彼此不可见。原理如下:
解决
使用synchronized关键字。
public class Test { public static void main(String[] args) { DemoThread thread = new DemoThread(); new Thread(thread).start(); while (true) { synchronized (thread) { if (thread.isFlag()) { System.out.println("---------------"); break; } } } } } class DemoThread implements Runnable { private boolean flag = false; @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } setFlag(true); System.out.println("flag is " + isFlag()); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
但是synchronized关键字效率低,当第一个线程获取到锁之后,之后的线程会对锁进行检测,如果锁被占用,则会被阻塞,然后等待后续的cpu调度执行。有没有更好的方法呢?
volatile
volatile关键字的作用是当多个线程进行操作共享数据时,可以保证内存中的数据可见。我们也可以将其理解为多个线程操作被volatile关键字修饰的数据时相当于直接操作的主存中的数据。这样对各个变量来说,这些被修饰的数据都是实时可见的。但是volatile关键字的效率也低,被volatile关键字修饰的变量不能进行JVM底层的优化(重排序),但是其效率是比synchronized关键字高的。volatile相较于synchronized是一种较为轻量级的同步策略。
public class TestVolatile { public static void main(String[] args) { DemoThread thread = new DemoThread(); new Thread(thread).start(); while (true) { if (thread.isFlag()) { System.out.println("---------------"); break; } } } } class DemoThread implements Runnable { private volatile boolean flag = false; @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } setFlag(true); System.out.println("flag is " + isFlag()); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
注意
1.volatile关键字不具备互斥性。synchronized具备互斥性。
2.volatile关键字仅仅能保证变量写操作的原子性,不保证复合操作,比如说读写操作的原子性