何时可用 volatile - 点滴记忆*记忆点滴
收藏本站

何时可用 volatile

volatile 作用和简介,参考此处

众所周知: volatile 用于修饰可能意外改变的类变量。常用的就是多线程,volatile 在部分时候可用替代 synchronized ,但要记住这是要牺牲性能的,因为其阻止了jvm 对执行命令的优化。


下面是一个验证使用volatile 的小例子,通过它来加深对volatile 作用的理解:


/**
 * Volatile 作用测试类
 * @author TMSer
 * @2012-11-05
 */

public class VolatileTest extends Thread{
    public int c = 0;
    public VolatileTest(A a,int c){
        this.a  =  a;
        this.c = c;
    }
    private A a;
    public void run(){
        for(int i=0;i<10;i++){
            System.out.print("t"+c+" "+ a.setA(i)+" - ");
            try {
                sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("rt"+c+" "+a.getA());

        }
    }
    
    public static void main(String[] args) {
        Thread[] t = new VolatileTest[15];
        A a = new VolatileTest.A();
        for(int i=0;i<15;i++){
            t[i] = new VolatileTest(a,i);
        }
        
        for(int i=0;i<15;i++){
            t[i].start();
        }
        
        for(int i=0;i<15;i++){
            try {
                t[i].join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    public static class A{
        private int a = 0; //分别输出加volatile 和不加 volatile 的结果
        public int setA(int a){
            this.a = a;
            return a;
        }
        
        public int getA(){
            return a;
        }
    }
}


结果分析:(环境 jdk 6,xp,2G)

不加时输出结果:取了前两行


t0 0 - t4 0 - t1 0 - t8 0 - t7 0 - t2 0 - t3 0 - t6 0 - t12 0 - t11 0 - t10 0 - t14 0 - t9 0 - t13 0 - t5 0 - rt8 0
t8 1 - rt3 0   //在这行可看出,线程8运行完setA(1)后sleep,运行线程3的getA(),结果却为0.与预想结果不一致

....

加了volatile 后:取前三行

t0 0 - t2 0 - t1 0 - t3 0 - t4 0 - t5 0 - t6 0 - t7 0 - t8 0 - t9 0 - t10 0 - t11 0 - t12 0 - t13 0 - t14 0 - rt7 0
rt11 0
t11 1 - rt2 1  //这行可知,线程11运行了setA(1)后sleep,而后运行线程2的getA();结果为1。而在这之前并没有运行线程2 的setA(1)。与预想结果一致。

...


从上面例子可看看出使用了volatile关键字后在多线程环境下,可以得到预想结果,所有有人说其可以部分替代 synchronized ,那它的原理是什么及它和同步块有什么区别及该如何选择呢。

    如果变量被声明为volatile,在每次访问时都会和主存一致。这个一致性是由java语言保证的,并且是原子的,即使是64位的值。(注意很多JVM 没有正确的实现volatile关键字。你可以在www.javasoft.com找到更多的信息)。 另外,如果变量在同步方法或者同步块中被访问,当在方法或者块的入口处获得锁以及方法或者块退出时释放锁是变量被同步。
  变量访问的频度不同则你的选择的性能不同。如果你更新很多变量,那么使用volatile可能比使用同步更慢。记住,如果变量被声明为volatile,那么在每次访问时都会和主存一致。与此对照,使用同步时,变量只在获得锁和释放锁的时候和主存一致。但是同步使得代码有较少的并发性。所以如果你更新很多变量并且不想有每次访问都和主存进行同步的损失或者你因为其它的原因想排除并发性时可以考虑使用同步

    它的原理就是JVM对代码进行了优化,如何优化的从网上找的这文章讲的挺不错的。参考

    留下足迹