线程封闭

当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步。这种技术被称为线程封闭。它是实现线程安全最简单的方式之一。当某个对象封闭在一个线程中时,这种用法将自动实现线程安全性。即使被封闭的对象本身不是线程安全的。

1、栈封闭

栈封闭是线程封闭的一种特例,在栈封闭中,只能通过局部变量才能访问对象。局部变量的固有属性之一就是封闭在执行线程中。它们位于执行线程的栈中,其他线程无法访问这个栈。

基本类型的局部变量和引用变量的线程封闭性:

public int loadTheArk(Collection<Animal> candidates){
    SortedSet<Animal> animals;
    int numPairs = 0;
    Animal candidate = null;

    //animals被封闭在方法中,不要使它逸出
    animals = new TreeSet<Animal>(new SpeciesGenderComparator());
    animals.addAll(candidates);
    for(Animal a: animals){
        if(candidates == null || !candidate.isPotentialMate(a))
            candidate = a;
        else{
            ark.load(new AnimalPair(candidate, a);
            ++numPairs;
            candidate = null;
        }
    }
    return numPairs;
}

上诉代码中,基本类型的局部变量numPairs被封装在线程内,其他线程无论如何都访问不到该变量。

在方法中实例化一个TreeSet对象,并将指向该对象的一个引用保存在animals中。此时,只有一个引用指向集合animals,这个引用被封闭在局部变量中,因此也被封闭在执行线程中。然而,如果发布了animals, 封闭性就会被破坏。

2、ThreadLocal类

维持线程封闭的另一种更规范的方法是使用ThreadLocal,这个类能使线程中的某个值与保存值的对象关联起来。ThreadLocal类提供了get/set等方法,这些方法为每一个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的新值。

ThreadLocal对象通常用于防止对可变的但是离变量或全局变量进行共享。

例如,通过将JDBC的连接保存在ThreadLocal中,每个线程都会拥有属于自己的数据库连接。

private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>(){
    public Connection initialValue(){
        return DriverManager.getConnection(DB_URL);
    }
};

public static Connection getConnection(){
    return connectionHolder.get();
}

猜你喜欢

转载自my.oschina.net/HuoQibin/blog/1806386