首页 > 代码库 > java-多线程深入(四)Volatile分析

java-多线程深入(四)Volatile分析

(一)volatile的使用

1、使用场景

(1)状态标识。用于实时指示某个重要性事件的发生,比如完成初始化或者停机。

volatile boolean toShutdown;

    ......

    public void shutdown() { toShutdown = true; }

    public void doWork() { 
        while (!toShutdown) { 
            // 执行操作
        }
    }
(2)对象安全发布。volatile是处理对象安全发布的其中一种方式,更多内容参加这里。
class Singleton{
        private volatile static Singleton instance = null;
         
        private Singleton() {
             
        }
         
        public static Singleton getInstance() {
            if(instance==null) {
                synchronized (Singleton.class) {
                    if(instance==null)
                        instance = new Singleton();
                }
            }
            return instance;
        }
    }

经典的双重检查单例模式,volatile变量不会导致new对象的时候对象未初始化完成。

2、通常来说,使用volatile必须具备以下2个条件:
(1)对变量的写操作不依赖于当前值
(2)该变量没有包含在具有其他变量的不变式中

对于1,因为volatile变量不保证原子性,一个线程执行i++的同时另一个线程获取i的值,不一定能获取到最新值。

对于2,

    volatile int min = 0;
    volatile int max = 10;

    public boolean setA(int newMin) {
        if (newMin > max) {
            return false;
        }
        min = newMin;
        return true;
    }
    
    public boolean setB(int newMax) {
        if (newMax < min) {
            return false;
        }
        max = newMax;
        return true;
    }
一个线程调用setA(8),一个线程调用setB(2),可能会得到min=8、max=2,这与最初代码设计意图不符合。


(二)原理分析

volatile实现内存可见性原理:

volatile Singleton instance = new Singleton();

instance变量在进行写操作的时候,汇编下增加了lock指令。

lock指令的作用:

1.线程工作内存中的数据在进行写操作的时候,会立即回写到主内存中

2.回写到主内存时,让其他CPU中该数据的缓存失效,下次读取需要重新从主内存中获取

/**
 * Volatile的使用测试
 *
 * @author peter_wang
 * @create-time 2015-1-14 下午10:11:57
 */
public class ThreadVolatileDemo {
    private boolean isStop = false;

    private void changeStatus() {
        isStop = !isStop;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        final ThreadVolatileDemo test = new ThreadVolatileDemo();
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                System.out.println("尝试退出开始");
                while (true) {
                    //一直检测isStop变量是否更新
                    if (test.isStop) {//A1
                        System.out.println("退出成功");
                        System.exit(0);
                    }
                }
            }
        };
        thread1.start();

        try {
            Thread.sleep(3000);
            test.changeStatus();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
A1部分不断循环重复读取isStop值,但是读取的是线程工作内存中的数据,而且不断命中该数据,数据块不易被替换,增加读取到旧值的概率。修改isStop变量为volatile,变量被修改后会立即回写到主内存,A1读取线程工作内存失效,从主内存中读取到最新值。



java-多线程深入(四)Volatile分析