正確理解ThreadLocal幫助咱們更好的理解Spring,Hibernate

ThreadLocal<T>類在Spring,Hibernate等框架中起到了很大的做用,對於其工做原理,不少網上的文章分析的不夠完全,甚至有些誤解。 java

 

首先,爲了解釋ThreadLocal類的工做原理,必須同時介紹與其工做甚密的其餘幾個類(內部類) 多線程

1.ThreadLocalMap 框架

2.Thread ide

可能有人會以爲Thread與ThreadLocal有什麼關係,其實真正的奧祕就在Thread類中的一行: ui

 

Java代碼 複製代碼  收藏代碼
  1. ThreadLocal.ThreadLocalMap threadLocals = null;  

ThreadLocal.ThreadLocalMap threadLocals = null;

 

 其中ThreadLocalMap的定義是在ThreadLocal類中,真正的引用倒是在Thread類中 this

 

那麼ThreadLocalMap到底是什麼呢? spa

 

能夠看到這個類應該是一個Map,JDK的解釋是 prototype

 

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

 

接下來的重點是ThreadLocalMap中用於存儲數據的entry 線程

 

Java代碼 複製代碼  收藏代碼
  1. static class Entry extends WeakReference<ThreadLocal> {  
  2.             /** The value associated with this ThreadLocal. */  
  3.             Object value;  
  4.   
  5.             Entry(ThreadLocal k, Object v) {  
  6.                 super(k);  
  7.                 value = v;  
  8.             }  
  9.         }  

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是線程的名字或者標識 debug

 

到這裏,咱們就能夠理解ThreadLocal到底是如何工做的了

 

1.Thread類中有一個成員變量叫作ThreadLocalMap,它是一個Map,他的Key是ThreadLocal類

2.每一個線程擁有本身的申明爲ThreadLocal類型的變量,因此這個類的名字叫'ThreadLocal':線程本身的(變量)

3.此變量生命週期是由該線程決定的,開始於第一次初始(get或者set方法)

4.由ThreadLocal的工做原理決定了:每一個線程獨自擁有一個變量,並不是共享或者拷貝

 

Java代碼 複製代碼  收藏代碼
  1. /** 
  2.  * @author mxdba 
  3.  * 
  4.  */  
  5. public class ThreadLocalSample {  
  6.   
  7.     public static void main(String[] args) {  
  8.         ThreadTest test1 = new ThreadTest(10);  
  9.         ThreadTest test2 = new ThreadTest(20);  
  10.         test1.start();  
  11.         test2.start();  
  12.     }  
  13.   
  14. }  
  15.   
  16. /** 
  17.  * 此線程有兩個ThreadLocal變量,可是因爲ThreadLocal是延遲初始的, 
  18.  * 因此在debug時能夠看到線程名爲「線程20」的線程的ThreadLocalMap中沒有thLcal2這個entry 
  19.  * @author mxdba 
  20.  *  
  21.  */  
  22. class ThreadTest extends Thread {  
  23.       
  24.     public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();  
  25.     public static ThreadLocal<String> thLocal2 = new ThreadLocal<String>();  
  26.       
  27.     public Integer num;  
  28.       
  29.       
  30.       
  31.     public ThreadTest(Integer num) {  
  32.         super("線程" + num);  
  33.         this.num = num;  
  34.     }  
  35.   
  36.     @Override  
  37.     public void run() {  
  38.         Integer n = thLocal.get();  
  39.         if(num != 20) {  
  40.             String s = thLocal2.get();  
  41.         }  
  42.               
  43.         if(n == null) {  
  44.             thLocal.set(num);  
  45.         }  
  46.         System.out.println(thLocal.get());  
  47.     }  
  48.       
  49. }  

/**
 * @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());
	}
	
}

 

 

接下來分析一下源碼,就更加清楚了

 

關鍵方法代碼 複製代碼  收藏代碼
  1. /**  
  2.  * 關鍵方法,返回當前Thread的ThreadLocalMap  
  3.  * [[[每一個Thread返回各自的ThreadLocalMap,因此各個線程中的ThreadLocal均爲獨立的]]]  
  4.  */  
  5. ThreadLocalMap getMap(Thread t) {  
  6.         return t.threadLocals;  
  7.     }  

/**
 * 關鍵方法,返回當前Thread的ThreadLocalMap
 * [[[每一個Thread返回各自的ThreadLocalMap,因此各個線程中的ThreadLocal均爲獨立的]]]
 */
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

 

 

 

 

Threadlocal的get方法代碼 複製代碼  收藏代碼
  1. public T get() {  
  2.         Thread t = Thread.currentThread();  
  3.         /**  
  4.          * 獲得當前線程的ThreadLocalMap  
  5.          */  
  6.         ThreadLocalMap map = getMap(t);  
  7.         if (map != null) {  
  8.             /**  
  9.              * 在此線程的ThreadLocalMap中查找key爲當前ThreadLocal對象的entry  
  10.              */  
  11.             ThreadLocalMap.Entry e = map.getEntry(this);  
  12.             if (e != null)  
  13.                 return (T)e.value;  
  14.         }  
  15.         return setInitialValue();  
  16.     }  

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();
    }

 

 

 

 

初始化方法代碼 複製代碼  收藏代碼
  1. private T setInitialValue() {  
  2.         /**  
  3.          * 默認返回null,這個方法爲protected能夠繼承  
  4.          */  
  5.         T value = initialValue();  
  6.         Thread t = Thread.currentThread();  
  7.         ThreadLocalMap map = getMap(t);  
  8.         if (map != null)  
  9.             map.set(this, value);  
  10.         else  
  11.             /**  
  12.              * 初次建立  
  13.              */  
  14.             createMap(t, value);  
  15.         return value;  
  16.     }  

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;
    }

 

 

 

Java代碼 複製代碼  收藏代碼
  1. /** 
  2.  * 給當前thread初始ThreadlocalMap 
  3.  */  
  4. void createMap(Thread t, T firstValue) {  
  5.         t.threadLocals = new ThreadLocalMap(this, firstValue);  
  6.     }  

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

 

 

 

經過上邊的分析,咱們發現,ThreadLocal類的使用雖然是用來解決多線程的問題的,可是仍是有很明顯的針對性

1.最明顯的,ThreadLoacl變量的活動範圍爲某線程,而且個人理解是該線程「專有的,獨自霸佔」,對該變量的全部操做均有該線程完成!也就是說,ThreadLocal不是用來解決共享,競爭問題的。典型的應用莫過於Spring,Hibernate等框架中對於多線程的處理了

 

Java代碼 複製代碼  收藏代碼
  1. private static final ThreadLocal threadSession = new ThreadLocal();    
  2.     
  3. public static Session getSession() throws InfrastructureException {    
  4.     Session s = (Session) threadSession.get();    
  5.     try {    
  6.         if (s == null) {    
  7.             s = getSessionFactory().openSession();    
  8.             threadSession.set(s);    
  9.         }    
  10.     } catch (HibernateException ex) {    
  11.         throw new InfrastructureException(ex);    
  12.     }    
  13.     return s;    
  14. }    

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,而該類實例的方法都可以共享。

 

2.關於內存泄漏:

雖然ThreadLocalMap已經使用了weakReference,可是仍是建議可以顯示的使用remove方法。

相關文章
相關標籤/搜索