ThreadLocal深入理解

ThreadLocal从字面上理解,很容易会把ThreadLocal误解为一个线程的本地变量。ThreadLocal并不是代表当前线程,ThreadLocal其实是采用哈希表的方式来为每个线程都提供一个变量的副本。从而保证各个线程间数据安全。每个线程的数据不会被另外线程访问和破坏。每个线程都拥有一个自己的ThreadLocalMap对象,而这个对象的key是ThreadLocal类型的,这个key在每个线程中都是一样的,ThreadLocal只是作为一个索引。之所以有线程局部变量这一说,是因为每个线程的ThreadLocalMap对象不同,那ThreadLocal类型的key当然可以指向不同对象了。

 1 ThreadLocalMap只是ThreadLocal的一个静态内部类,而在该静态内部类里面又有一个静态内部类Entry,创建一个ThreadLocalMap ,需要两个参数,一个是ThreadLocal对象,作为key,一个是value,作为值。可以通过key来查到value

public class ThreadLocal<T> {

    static class ThreadLocalMap {

         static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

        ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

 

}

         ........

 

}

 

2  每一个Thread类里面都会有一个ThreadLocalMap的属性对象,该map用来存储value,这样每一个Thread对象就会有一个自己的value,而不与其他线程冲突。当然前提是该value不是多线程共享的对象,而是局部对象,如果是多线程共享对象,依然会有线程共享问题,用ThreadLocal解决问题的关键是ThreadLocalMap存储的对象是非共享对象,这样才会每个线程有自己的对象,修改这个对象不对其他线程造成困扰。

public  class Thread implements Runnable {

 

    ThreadLocal.ThreadLocalMap threadLocals = null;

    ...........................

 

 

}

 

3  看看ThreadLocal的set和get方法

 

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

 

 

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

通过源码,我们可以发现,ThreadLocal的get方法是从每个线程的ThreadLocalMap对象去获取value,其中ThreadLocal是作为key。set是把value放到ThreadLocalMap中去。

 

4  ThreadLocal能实现各个线程用到各自的value而不对其他线程造成冲突,是因为每一个线程对象有一个自己ThreadLocalMap,该map是以ThreadLocal作为key,来保存和获取value,当然前提不冲突是必须保存的value不能是多线程共享对象。

 

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

以下是hibernate对于ThreadLocal的运用片段:

 

 

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。但是一般的应用服务器都会维护一套线程池。因此,不同用户访问,可能会接受到同样的线程。因此,在做基于TheadLocal时,需要谨慎,避免出现ThreadLocal变量的缓存,导致其他线程访问到本线程变量。比如在jboss中,就有线程池,线程并不是处理完就销毁的,而是放回池里重用。所以,在你处理完一次request-response的时候,一定不要忘了手动删除ThreadLocal里的变量,也就是说手动调用一次threadLocalVar.remove()。  

 

 

 

你可能感兴趣的:(多线程)