当前位置:编程学习 > 网站相关 >>

使用Immutable对象解决线程安全

何为Immutable对象?
 
简单地说,如果一个对象实例不能被更改就是一个Immutable的对象,Java SDK提供的大量值对象,比如String等都是Immutable的对象。
 
如何使对象Immutable?
 
按照Effective Java的说明,需要满足下面几条规则:
 
保证类不能被继承- 为了避免其继承的类进行mutable的操作
移调所有setter/update等修改对象实例的操作
保证所有的field是private和final的
为什么要采用Immutable对象?
 
在并发程序中,使用Immutable可以既保证线程安全性,跟并发锁方式相比,它大大增强了并发时的效率。尤其当一个对象是值对象时,更应该考虑采用Immutable方式。
 
为了说明,这里先举一个Mutable的非线程安全的例子。person应该是一个典型的值对象,但下面的例子没有使他具备Immutable特性。
 
 
//non thread-safe 
 
public class ImmutableDemo { 
    static MutablePerson testM = new MutablePerson("joanieM", 14); 
     
    public static void main(String[] args) { 
        Thread t1 = new MutableTestThread(1); 
        t1.start(); 
        Thread t2 = new MutableTestThread(2); 
        t2.start(); 
    } 

 
class MutablePerson { 
    private int age; //Rule 1: all fields are private and final 
    private String name; 
     
    public MutablePerson(String name, int age) { //rule 2: a factory method pattern is adopted to create the object 
        this.age = age; 
        this.name = name; 
    } 
 
    public String getName() { 
        return name; 
    } 
     
    public int getAge() { 
        return age; 
    } 
 
    public String toString() { 
        return name +": "+age +"year(s) old"; 
    } 
     
    public void updatePerson(String name, int age){ 
        this.name = name; 
        this.age = age; 
        System.out.println(this); 
    } 

 
class MutableTestThread extends Thread { 
    final int MAX=10;  
    final int idx; 
 
    public MutableTestThread(int idx) { 
        this.idx = idx; 
    } 
 
    public void run() { 
        for (int i = 0; i < MAX; i++) { 
            ImmutableDemo.testM.updatePerson("joanieM"+idx, idx*MAX+i); 
             
            try { 
                Thread.sleep(20+i*2); 
            } catch (InterruptedException e) { 
                // TODO Auto-generated catch block 
                e.printStackTrace(); 
            } 
        } 
    } 

运行结果:
joanieM1: 10year(s) old
joanieM2: 20year(s) old
joanieM2: 11year(s) old
joanieM2: 11year(s) old
joanieM2: 22year(s) old
joanieM2: 22year(s) old
joanieM2: 13year(s) old
joanieM2: 13year(s) old
joanieM2: 14year(s) old
joanieM2: 14year(s) old
joanieM1: 15year(s) old
joanieM2: 25year(s) old
joanieM1: 16year(s) old
joanieM1: 16year(s) old
joanieM2: 27year(s) old
joanieM1: 27year(s) old
joanieM1: 18year(s) old
joanieM2: 28year(s) old
joanieM2: 29year(s) old
joanieM1: 19year(s) old
结果中红颜色标注的都是错误的结果。由于没有采取任何保证线程安全性的操作,首先线程t1执行完updatePerson函数的this.age=age后被挂起,线程t2执行完updatePerson函数的this.name=name后被挂起,线程t1继续执行,此时的name值为t2执行的结果joanieM2,age则为t1执行的结果,于是打印出了错误的值:joanieM2: 11year(s) old
 
下面的例子给出了如何使用Immutable来保证值对象的线程安全性的。
 
 
public class ImmutableDemo { 
    //test is a shared thread-safe object.  
    static ImmutablePerson test = ImmutablePerson.getPerson("joanie", 14); 
     
    public static void main(String[] args) { 
        Thread t1 = new TestThread(1); 
        t1.start(); 
        Thread t2 = new TestThread(2); 
        t2.start(); 
    } 

 
//a sample immutable class 
//Rule 4: define class as final one 
final class ImmutablePerson { 
    private final int age; //Rule 1: all fields are private and final 
    private final String name; 
     
    private ImmutablePerson(String name, int age) { //rule 2: a factory method pattern is adopted to create the object 
        this.age = age; 
        this.name = name; 
        System.out.println(this); 
    } 
 
    public String getName() { 
        return name; 
    } 
     
    public int getAge() { 
        return age; 
    } 
 
    public String toString() { 
        return name +": "+age +"year(s) old"; 
    } 
    //Rule 3: no setters for value update. Create a new class instead 
    public static ImmutablePerson getPerson(String name, int age) { 
        return new ImmutablePerson(name, age); 
    }  

 
class TestThread extends Thread { 
    final int MAX=10;  
   
补充:综合编程 , 安全编程 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,