ThreadLocal变量

来源:互联网 发布:ubuntu离线安装ssh 编辑:程序博客网 时间:2024/06/06 01:54

    ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

    ThreadLocal类接口包含以下四个方法:(新版本的ThreadLocal已经支持泛型)   ThreadLocal<T>

       public T get()  : 返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用initialValue方法的返回值。

       protected T initialValue() :返回此线程局部变量的当前线程的“初始值”。线程第一次使用get()方法访问变量时先调用此方法,但如果线程之前调用了set()方法,则不会对该线程在调用initialValue()方法。通常,此方法对每个线程最多调用一次,但如果在调用get()后又调用了remove()方法,则可能再次调用此方法。该实现返回null;如果希望返回null以为的值,则必须为ThreadLocal创建子类,并重写该方法。通常将使用匿名内部类来完成此操作。

        public void remove() :移除此线程局部变量当前线程的值。如果此线程局部变量随后被当前线程读取,且这期间当前线程没有设置其值,则将调用initialValue方法重新初始化其值。这将导致在当前线程多次调用initialValue方法。

        public void set(T value) :将此线程局部变量的当前线程副本中的值设置为指定的值。大部分子类不需要重写该方法,只需依靠initialValue方法来设置线程局部变量的值

   ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Tread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的,且ThreadLocal实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

    ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

    概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。下面是一个ThreadLocal的应用实例:

public class SequenceNumber {private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){ @Overridepublic Integer initialValue(){return 0;}};public int getNextNum(){seqNum.set(seqNum.get()+1);return seqNum.get();}public static void main(String[] args) {// TODO Auto-generated method stubSequenceNumber sn = new SequenceNumber();TestClient t1 = new TestClient(sn);TestClient t2 = new TestClient(sn);TestClient t3 = new TestClient(sn);t1.start();t2.start();t3.start();}private static class TestClient extends Thread{private SequenceNumber sn;public TestClient(SequenceNumber sn){this.sn = sn;}public void run(){  for (int i = 0; i < 3; i++) {System.out.println("thread["+Thread.currentThread().getName()+"] sn["+sn.getNextNum()+"]");}} }}

原创粉丝点击