随笔-3  评论-0  文章-0  trackbacks-0
volatile:具有 synchronized 的可见性特性,但是不具备原子特性.这就是说线程能 够自动发现 volatile 变量的最新值.

使用volatile原则:

  对变量的写操作不依赖于当前值。

  该变量没有包含在具有其他变量的不变式中


package
 zhang.feng.concurrent.atomic;

public class VolatileTest {
    
    
private String str="";
    
    
public String getStr() {
        
return str;
    }
    
public void setStr(String str) {
        
this.str = str;
    }
    
private void doit(){
        
        
new Thread(new Runnable(){
            
public void run() {
                setStr(
"没有写入主存中");
                System.out.println(
"====");
            }
        },
"THREAD_ONE").start();
        
new Thread(new Runnable(){
            
public void run() {
                String str
=getStr();
                System.out.println(str);
            }
        },
"THREAD_TWO").start();
    }
    
    
public static void main(String[] args){
        VolatileTest vt
=new VolatileTest();
        vt.doit();
    }
}

上面的例子,如果不使用volatile变量,可能出现的情况是(出现几率很小),THREAD_ONE线程在setStr()后,
THREAD_TWO执行了getStr(),但是由于并没有将数据写入主存中,而此时getStr()获得的数据将是过时数据.

解决的方式,通过lock(开销比较大),使用volatile变量(如上).




正确使用 volatile 的模式
1.状态标志
volatile boolean shutdownRequested;



public void shutdown() { shutdownRequested = true; }

public void doWork() { 
    
while (!shutdownRequested) { 
        
// do stuff
    }
}
可能在另一个线程中调用shutdown(),那么使用volatile可以保证shutdownRequested可见性,所以不需要使用lock来实现数据同步.
该模式特征:只有一种状态的转换.

2.
一次性安全发布
public class BackgroundFloobleLoader {
    
public volatile Flooble theFlooble;

    
public void initInBackground() {
        
// do lots of stuff
        theFlooble = new Flooble();  // this is the only write to theFlooble
    }
}

public class SomeOtherClass {
    
public void doWork() {
        
while (true) { 
            
// do some stuff
            
// use the Flooble, but only if it is ready
            if (floobleLoader.theFlooble != null
                doSomething(floobleLoader.theFlooble);
        }
    }
}
在如果initInBackground(写入)和doWork(读取)在不同线程同时执行时,那么可能获得的更新后的Flooble对象也可能是未更新前的,而是用volatile可以保证是读取到更新后的对象.

该模式的一个必要条件是:被发布的对象必须是线程安全的,或者是有效的不可变对象(有效不可变意味着对象的状态在发布之后永远不会被修改)。



3.
独立观察
public class UserManager {
    
public volatile String lastUser;

    
public boolean authenticate(String user, String password) {
        
boolean valid = passwordIsValid(user, password);
        
if (valid) {
            User u 
= new User();
            activeUsers.add(u);
            lastUser 
= user;
        }
        
return valid;
    }
}

该模式可以用于收集最近一次登录信息,并提交其他应用处理.

4.“volatile bean” 模式
@ThreadSafe
public class Person {
    
private volatile String firstName;
    
private volatile String lastName;
    
private volatile int age;

    
public String getFirstName() { return firstName; }
    
public String getLastName() { return lastName; }
    
public int getAge() { return age; }

    
public void setFirstName(String firstName) { 
        
this.firstName = firstName;
    }

    
public void setLastName(String lastName) { 
        
this.lastName = lastName;
    }

    
public void setAge(int age) { 
        
this.age = age;
    }
}

结束语
与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。本文介绍的模式涵盖了可以使用 volatile 代替 synchronized 的最常见的一些用例。遵循这些模式(注意使用时不要超过各自的限制)可以帮助您安全地实现大多数用例,使用 volatile 变量获得更佳性能。
posted on 2010-07-15 16:02 QZZF 阅读(115) 评论(0)  编辑  收藏