volatile变量的主要作用:是使变量在多个线程间可见。
在java中每一个线程都会有一块工作内存区,其中存放着所有线程共享的主内存的变量值的拷贝。当线程执行时,它在自己的工作内存区操作这些变量,为了读取一个共享的变量,一个线程会获取锁定并去清除它的内存工作区,把这些共享变量从所有线程的共享工作区中正确的装入到它自己所在的工作内存区中,当线程解锁时保证该工作内存区中变量的值写回到共享内存中。
volatile的作用就是强制线程到主内存(共享内存)里读取数据,而不去工作线程区里去读取,从而实现了多个线程间变量可见。也就是满足了线程安全的可见性。
当然用synchronized也能实现volatile的功能,但是仅仅为了读写一个或两个实例域就是用synchronized,会让CPU开销很大。
举个栗子:
1 public class RunThread extends Thread{ 2 3 private volatile boolean isRunning = true; 4 private void setRunning(boolean isRunning){ 5 this.isRunning = isRunning; 6 } 7 8 public void run(){ 9 System.out.println("进入run方法.."); 10 int i = 0; 11 while(isRunning == true){ 12 //.. 13 } 14 System.out.println("线程停止"); 15 } 16 17 public static void main(String[] args) throws InterruptedException { 18 RunThread rt = new RunThread(); 19 rt.start(); 20 Thread.sleep(1000); 21 rt.setRunning(false); 22 System.out.println("isRunning的值已经被设置了false"); 23 } 24 25 26 }
在这个栗子中,创建子线程 rt 并启动后,就让主线程休眠 1s,让子线程 rt 运行,因为此时 isRunning为true,所以 rt 子线程会一直循环执行下去。当主线程休眠1s结束,将 isRunning的值改为了false,rt 子线程不在执行循环,rt子线程结束。执行结果为:
volatile虽然拥有多个线程之间的可见性,但是却不具备同步性(即原子性),一般volatile用于只针对多个线程可见的变量操作,并不能代表synchronized的同步操作。
举个栗子:
1 package com.song.test; 2 3 public class TestVolatile extends Thread { 4 public volatile static int n = 0; 5 6 public static void main(String[] args) { 7 TestVolatile t1 = new TestVolatile(); 8 t1.start(); 9 for (int i = 0; i < 200000; i++) { 10 n=n+1; 11 System.out.println(n); 12 } 13 14 } 15 16 public void run() { 17 for (int i = 0; i < 200000; i++) { 18 n=n+1; 19 System.out.println(n); 20 } 21 } 22 }
在这个例子中,让 n 通过主线程和子线程分别加了两回,每次加了20000,如果是原子性操作那么n最终的值应该是400000,但实际上它的值是399998,因为它是非原子性的:
结果为:
那怎么用volatile关键字呢。对volatile修饰的变量要所有筛选:
变量的读写操作是独立的,不依赖其当前值(如 n=n+1),也不依赖于其他状态状态变量的共同参与与不变约束。