ThreadLocal总结

原文: ThreadLocal-分析-总结  网上虽然很多关于ThreadLocal , 但是这篇文章,从

源码角度进行剖析 ,让你不得不信服,Thanks!

以前,对ThreadLocal 错误的理解:

 它内部维护了一个map,然后,这个map的key是本地线程的标志,value则是线程不安全的对象

 其实,不是这样的:

 1. ThreadLocal 中有个内部类,ThreadLocalMap ,但是它并没有map这个成员变量,它只是对map进行操作!

      map则是存在于各个线程中的。查看Thread的源代码就可以看到每个Thread都有一个ThreadLocal的成员变量!

 2.   ThreadLocal中的key不是线程的标志,而是TheadLocal这个对象,map不是在TheadLocal中!!!

 3. 每个线程中都有一个ThreadLocalMap , map的key为ThreadLocal对象,这样不管你有多少个ThreadLocal , 线程中

     都会存储你相应的值! 

 4. 这样看的话,ThreadLocal 的作用,就会使某个线程一直占用着某个资源!并不是数据的共享!

     当然,如果你set的对象是一个线程间共享的对象,那就另当别论了。比如 只要一个Node n , 而所有线程都是set的这个!

    不过,这就与ThreadLocal的使用场景不同!它里面存放是不同的对象!每个线程都是不同的!

 5.将map绑定到当前线程的好处:

     当前线程销毁的时候,相应的map也销毁了,这里,要注意,线程池的情况!!!

     各线程访问的map是各自不同的map,所以不需要同步,速度会快些;而如果把所有线程要用的对象都放到一个静态map中的话 多线程并发访问需要进行同步。

 5.ThreadLocal 与 synchronized的区别:

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

当然ThreadLocal并不能替代synchronized ,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。

 

 图片出处: http://www.cnblogs.com/onlywujun/p/3524675.html 

 

下面是原文:

ThreadLocal<T>类在Spring,Hibernate等框架中起到了很大的作用,对于其工作原理,很多网上的文章分析的不够彻底,甚至有些误解。

首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作甚密的其他几个类(内部类)

1.ThreadLocalMap

2.Thread

可能有人会觉得Thread与ThreadLocal有什么关系,其实真正的奥秘就在Thread类中的一行:

ThreadLocal.ThreadLocalMap threadLocals = null;  

   其中ThreadLocalMap的定义是在ThreadLocal类中,真正的引用却是在Thread类中

   那么ThreadLocalMap究竟是什么呢?

可以看到这个类应该是一个Map,JDK的解释是

ThreadLocalMap is a customized hash map suitable only for maintaining thread local values

接下来的重点是ThreadLocalMap中用于存储数据的entry

static class Entry extends WeakReference<ThreadLocal> {  
            /** The value associated with this ThreadLocal. */  
            Object value;  
  
            Entry(ThreadLocal k, Object v) {  
                super(k);  
                value = v;  
            }  
        } 

从中我们可以发现这个Map的key是ThreadLocal变量,value为用户的值,并不是网上大多数的列子key是线程的名字或者标识

到这里,我们就可以理解ThreadLocal究竟是如何工作的了

1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类

2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)

3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)

4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝

/** 
 * @author mxdba 
 * 
 */  
public class ThreadLocalSample {  
  
    public static void main(String[] args) {  
        ThreadTest test1 = new ThreadTest(10);  
        ThreadTest test2 = new ThreadTest(20);  
        test1.start();  
        test2.start();  
    }  
  
}  
  
/** 
 * 此线程有两个ThreadLocal变量,但是由于ThreadLocal是延迟初始的, 
 * 所以在debug时可以看到线程名为“线程20”的线程的ThreadLocalMap中没有thLcal2这个entry 
 * @author mxdba 
 *  
 */  
class ThreadTest extends Thread {  
      
    public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();  
    public static ThreadLocal<String> thLocal2 = new ThreadLocal<String>();  
      
    public Integer num;  
      
      
      
    public ThreadTest(Integer num) {  
        super("线程" + num);  
        this.num = num;  
    }  
  
    @Override  
    public void run() {  
        Integer n = thLocal.get();  
        if(num != 20) {  
            String s = thLocal2.get();  
        }  
              
        if(n == null) {  
            thLocal.set(num);  
        }  
        System.out.println(thLocal.get());  
    }  
      
}   

接下来分析一下源码,就更加清楚了

/**  
 * 关键方法,返回当前Thread的ThreadLocalMap  
 * [[[每个Thread返回各自的ThreadLocalMap,所以各个线程中的ThreadLocal均为独立的]]]  
 */  
ThreadLocalMap getMap(Thread t) {  
        return t.threadLocals;  
    }  

 get方法源代码:

  

public T get() {  
        Thread t = Thread.currentThread();  
        /**  
         * 得到当前线程的ThreadLocalMap  
         */  
        ThreadLocalMap map = getMap(t);  
        if (map != null) {  
            /**  
             * 在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry  
             */  
            ThreadLocalMap.Entry e = map.getEntry(this);  
            if (e != null)  
                return (T)e.value;  
        }  
        return setInitialValue();  
    }  

  

    初始化代码:

   

private T setInitialValue() {  
        /**  
         * 默认返回null,这个方法为protected可以继承  
         */  
        T value = initialValue();  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null)  
            map.set(this, value);  
        else  
            /**  
             * 初次创建  
             */  
            createMap(t, value);  
        return value;  
    }  

  

/** 
 * 给当前thread初始ThreadlocalMap 
 */  
void createMap(Thread t, T firstValue) {  
        t.threadLocals = new ThreadLocalMap(this, firstValue);  
    }  

  

通过上边的分析,我们发现,ThreadLocal类的使用虽然是用来解决多线程的问题的,但是还是有很明显的针对性

 使用场景:

1.最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。典型的应用莫过于Spring,Hibernate等框架中对于多线程的处理了

private static final ThreadLocal threadSession = new ThreadLocal();    
    
public static Session getSession() throws InfrastructureException {    
    Session s = (Session) threadSession.get();    
    try {    
        if (s == null) {    
            s = getSessionFactory().openSession();    
            threadSession.set(s);    
        }    
    } catch (HibernateException ex) {    
        throw new InfrastructureException(ex);    
    }    
    return s;    
}    

 这段代码,每个线程有自己的ThreadLocalMap,每个ThreadLocalMap中根据需要初始加载threadSession,这样的好处就是介于singleton与prototype之间,应用singleton无法解决线程,应用prototype开销又太大,有了ThreadLocal之后就好了,对于需要线程“霸占”的变量用ThreadLocal,而该类实例的方法均可以共享。(例如,某个成员变量,我们要独占,就可以用ThreadLocal , 这样看的话,ThreadLocal类,不就可以看成

 一个可以实现独占功能类!)

  

试想如果不用ThreadLocal怎么来实现呢?
可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。而ThreadLocal则是将变量绑定到当前线程,只要是在这个线程中就可以访问得到。
或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,
但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例
作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。
2、存放当前session用户:quake want的jert
3、存放一些context变量,比如webwork的ActionContext

3.关于内存泄漏:

虽然ThreadLocalMap已经使用了weakReference,但是还是建议能够显示的使用remove方法。

猜你喜欢

转载自kainever7.iteye.com/blog/2210331