Java中volatile关键字的效果

来源:互联网 发布:最好家庭网络理财投资 编辑:程序博客网 时间:2024/05/29 19:07

 

使用volatile和不使用volatile的区别在于JVM内存主存和线程工作内存的同步之上。volatile保证变量在线程工作内存和主存之间一致。以下是示例程序,成员变量boolValue用volatile修饰会导致程序很快退出:

使用volatile和不使用volatile的区别在于JVM内存主存和线程工作内存的同步之上。volatile保证变量在线程工作内存和主存之间一致。以下是示例程序,成员变量boolValue用volatile修饰会导致程序很快退出:

Java代码  
package  linyumin.test.thread;      
/**     
 *      
 * @author llade     
 *     
 */      
public   class  VolatileObjectTest {      
     
    /**      
     * 成员变量boolValue使用volatile和不使用volatile会有明显区别的。     
     * 本程序需要多试几次,就能知道两者之间的区别的。     
     * @param args     
     */      
    public   static   void  main(String[] args) {      
        final  VolatileObjectTest volObj= new  VolatileObjectTest();      
        Thread t1=new  Thread(){      
            public   void  run(){      
                System.out.println("t1 start" );      
                for (;;){      
                        volObj.waitToExit();      
                }      
            }      
        };      
        t1.start();      
        Thread t2=new  Thread(){      
            public   void  run(){      
                System.out.println("t2 start" );      
                for (;;){      
                    volObj.swap();      
                }      
            }      
        };      
        t2.start();      
    }      
     
    boolean  boolValue; //加上volatile 修饰的是时候,程序会很快退出,因为volatile 保证各个线程工作内存的变量值和主存一致。所以boolValue == !boolValue就成为了可能。       
     
    public   void  waitToExit() {      
        if (boolValue == !boolValue)System.exit( 0 ); //非原子操作,理论上应该很快会被打断。实际不是,因为此时的boolValue在线程自己内部的工作内存的拷贝,因为它不会强制和主存区域同步,线程2修改了boolValue很少有机会传递到线程一的工作内存中。所以照成了假的“原子现象”。       
    }      
          
    public   void  swap() { //不断反复修改boolValue,以期打断线程t1.       
        boolValue = !boolValue;      
    }      
     
}   

 

 

 

 首先每个线程都有自己一个工作内存区,多个线程共享一个主内存区。线程中的本地变量存在自己的内存区中,如for(int   i=0;i <100;i++){this.i=i;},其中i就存在线程工作内存中,即每个线程都有一个,不用也不能加volatile关键字,this.i就是共享变量。而共享的变量就存在主内存区里,但Java线程为了提高效率,会把共享变量拷贝到自己的工作区中,这就产生了变量一致性的问题。 
java提供的一种方法是互斥访问,互斥访问会在加锁和解锁中维持变量的一致性,另一种就是volatile关键字。 

java   language   specification中的一个例子,有类如下: 
class   Test   { 
    static   int   i   =   0,   j   =   0; 
    static   void   one()   {   i++;   j++;   } 
    static   void   two()   { 
                                          System.out.println( "i= "   +   i   +   "   j= "   +   j); 
                                        } 

有两个线程,一个不停调用   one(),一个不停调用   two(),则有可能出现这种情况,打印出来的j比i还大。因为这时线程对共享变量的更新是无序的。 
1.使用同步方法: 
class   Test   { 
    static   int   i   =   0,   j   =   0; 
    static   synchronized   void   one()   {   i++;   j++;   } 
    static   synchronized   void   two()   { 
                                                                    System.out.println( "i= "   +   i   +   "   j= "   +   j); 
                                                                    } 

这就不用我介绍了,i和j始终一样大。 
2.使用volatile 
class   Test   { 
    static   volatile   int   i   =   0,   j   =   0; 
    static   void   one()   {   i++;   j++;   } 
    static   void   two()   { 
                                          System.out.println( "i= "   +   i   +   "   j= "   +   j); 
                                          } 

这样能允许one()和two()并发执行,同时使one()如字面一样执行。这一般能使打印出来的j不会大于i,在更新j之前会先更新i。但有可能打印出来的j比i大很多,因为one()可能在two获取i和j之间执行了很多次。 

说明一下: 
        java   language   specification中的例子不好,有点晦涩,但我认为volatile的作用就是保证任何时候主内存的i都大于等于j。two()中出现j比i大很多,只是因为它访问的是不同时刻的主内存。 
        还有一点,就是volatile能防止编译器对变量进行优化,每次共享变量都到主内存。如果线程不到主内存中读,变量的值就会不正确。 
        强制线程到主存中读,应该也包含上面wulemale所说的,变量被程序外部改变的情况。

 

原创粉丝点击