一直都知道有ThreadLocal这么一个类,用来在多线程编程中为每一个Thread保存一份独立的数据,但是一直没有研究为什么这么来实现。今天看了下源码,发现跟之前了解的基本上差不多。

  ThreadLocal对象针对每一个Thread有一份独立的数据,实际上就是为每一个Thread创建了一个Map对象,这个Map对象中存储这个线程中所有设置的数据。

  ThreadLocal有四个比较关键的方法:

    T initialValue()  :返回初始化该变量的值,在get()的时候,如果没有设置过值,调用该方法初始化并返回。默认返回null,用于子类覆盖。

    T get()     :获取该变量当前值

    void set(T)   :设置该变量的值

    void remove()  :删除该变量的值

  下边是get及相关方法源码:

public T get() {
        Thread t = Thread.currentThread();  //获取当前线程
        ThreadLocalMap map = getMap(t);  //根据当前线程获取值map,其实就是返回了Thread内部的一个map。
        if (map != null) {  //如果已存在map,获取,并返回当前ThreadLocal对象对应值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();  //如果不存在map,或者当前ThreadLocal无对应的值,那么返回初始化。
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;  //返回Thread内部的map。
    }

    private T setInitialValue() {
        T value = initialValue();  //调用初始化方法,初始化当前ThreadLocal对应值
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

  看上面的源码基本上就可以了解set和remove的内部实现了,再次就不贴了。

  从上面的getMap方法可以看出,ThreadLocal并没有在内部去存储线程的数据,而仅仅只是线程内部的数据的一个代理类,这样做可以清晰而又简单的表现出数据对象线程隔离的效果。同时,由于数据存储在线程内部,那么数据会一直伴随着线程直到线程结束,所以可以通过这种方式来在一个线程中进行值传递操作,Spring中的对有状态的Bean的共享就是使用ThreadLocal实现的。

 

  当然,ThreadLocal经常用来解决并发问题,那么与同步机制有什么样的不同呢。

  性能上来说,ThreadLocal没有锁的使用,性能上会比同步好很多,而且由于不用关心并发控制,所以编程的复杂度大为降低,但是由于每一个线程都会有一个副本,这样相对于同步机制来说,消耗了大量的内存,总得来说就是一个空间换时间的结果。

  ThreadLocal与同步机制其实要解决的并不是同一类问题。同步机制实现了数据共享,用来解决多个线程对同一个资源的访问,同时实现不同线程间的通信。而ThreadLocal实现了数据隔离,用于多个线程的数据共享冲突情况。