Java并发编程实战学习笔记(三)-线程封闭

当访问共享数据时,通常需要使用同步。一种可以避免使用同步的方式就是不共享数据,如果仅在单线程内访问数据就不需要同步。这种技术被称为线程封闭

1. Ad-hoc线程封闭
Ad-hoc线程封闭指的是,维护线程封闭性的职责完全由程序来承担。Ad-hoc线程封闭是非常脆弱的,因为没有任何一种语言特性,例如可见性修饰符或局部变量,能将对象封闭到目标线程上。事实上,对线程封闭对象(例如,GUI应用程序中的可视化组件或数据模型等)的引用通常保存在公有变量中。
总结出来一句话 就是:由程序员自己写代码实现线程封闭。

2.栈封闭
局部变量的固有属性之一就是封闭在执行线程中,如果只能通过局部变量访问对象就实现了对象的栈封闭。栈封闭也被称为线程内部使用或者线程局部使用。
在下面程序段中 loadTheArk 方法的返回 numPairs 时是无法破坏栈的封闭性的。

import java.util.*;

/**
 * Animals
 *
 * Thread confinement of local primitive and reference variables
 *
 * @author Brian Goetz and Tim Peierls
 */
public class Animals {
    Ark ark;
    Species species;
    Gender gender;

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

        // animals confined to method, don't let them escape!
        animals = new TreeSet<Animal>(new SpeciesGenderComparator());
        animals.addAll(candidates);
        for (Animal a : animals) {
            if (candidate == null || !candidate.isPotentialMate(a))
                candidate = a;
            else {
                ark.load(new AnimalPair(candidate, a));
                ++numPairs;
                candidate = null;
            }
        }
        return numPairs;
    }


    class Animal {
        Species species;
        Gender gender;

        public boolean isPotentialMate(Animal other) {
            return species == other.species && gender != other.gender;
        }
    }

    enum Species {
        AARDVARK, BENGAL_TIGER, CARIBOU, DINGO, ELEPHANT, FROG, GNU, HYENA,
        IGUANA, JAGUAR, KIWI, LEOPARD, MASTADON, NEWT, OCTOPUS,
        PIRANHA, QUETZAL, RHINOCEROS, SALAMANDER, THREE_TOED_SLOTH,
        UNICORN, VIPER, WEREWOLF, XANTHUS_HUMMINBIRD, YAK, ZEBRA
    }

    enum Gender {
        MALE, FEMALE
    }

    class AnimalPair {
        private final Animal one, two;

        public AnimalPair(Animal one, Animal two) {
            this.one = one;
            this.two = two;
        }
    }

    class SpeciesGenderComparator implements Comparator<Animal> {
        public int compare(Animal one, Animal two) {
            int speciesCompare = one.species.compareTo(two.species);
            return (speciesCompare != 0)
                    ? speciesCompare
                    : one.gender.compareTo(two.gender);
        }
    }

    class Ark {
        private final Set<AnimalPair> loadedAnimals = new HashSet<AnimalPair>();

        public void load(AnimalPair pair) {
            loadedAnimals.add(pair);
        }
    }
}

即使在线程内部上下文中使用非线程安全的对象, 该对象仍然是线程安全的。但是在后续代码维护过程中很容易错误的时该不安全对象逸出,此时线程安全性就会遭到破坏。

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

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * ConnectionDispenser
 * 
 * Using ThreadLocal to ensure thread confinement
 *
 * @author Brian Goetz and Tim Peierls
 */
public class ConnectionDispenser {
    static String DB_URL = "jdbc:mysql://localhost/mydatabase";

    private ThreadLocal<Connection> connectionHolder
            = new ThreadLocal<Connection>() {
                public Connection initialValue() {
                    try {
                        return DriverManager.getConnection(DB_URL);
                    } catch (SQLException e) {
                        throw new RuntimeException("Unable to acquire Connection, e");
                    }
                };
            };

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

ThreadLocal常用于防止对可变的单实例变量或全局变量进行共享。在单线程应用程序中可能要维护一个全局的数据库连接,并在程序启动时初始化这个链接对象,从而避免在调用每个方法时都要传递一个Connection对象。
当某个频繁执行的操作需要一个临时对象时,例如一个缓冲区,而同时又希望避免在每次执行时都重新分配该临时对象,就可以使用该项技术。在实现应用程序框架时大量使用了ThreadLocal 。例如,在EJB 调用期间,J2EE 容器需要将一个事务上下文与某个执行中的线程关联起来。通过将事务上下文保存在静态的ThreadLocal 对象中,就可以实现这个功能:当框架代码需要判断当前运行的是哪一个事务时,只需要从这个ThreadLocal 对象中读取事务上下文。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值