ThreadLocal詳解(實現多線程同步訪問變量)


ThreadLocal翻譯成中文比較准確的叫法應該是:線程局部變量。

  這個玩意有什么用處,或者說為什么要有這么一個東東?先解釋一下,在並發編程的時候,成員變量如果不做任何處理其實是線程不安全的,各個線程都 在操作同一個變量,顯然是不行的,並且我們也知道volatile這個關鍵字也是不能保證線程安全的。那么在有一種情況之下,我們需要滿足這樣一個條件: 變量是同一個,但是每個線程都使用同一個初始值,也就是使用同一個變量的一個新的副本。這種情況之下ThreadLocal就非常使用,比如說DAO的數 據庫連接,我們知道DAO是單例的,那么他的屬性Connection就不是一個線程安全的變量。而我們每個線程都需要使用他,並且各自使用各自的。這種 情況,ThreadLocal就比較好的解決了這個問題。

  我們從源碼的角度來分析這個問題。

  首先定義一個ThreadLocal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ConnectionUtil {
     private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
     private static Connection initConn = null ;
     static {
         try {
             initConn = DriverManager.getConnection( "url, name and password" );
         } catch (SQLException e) {
             e.printStackTrace();
         }
     }
     
     public Connection getConn() {
         Connection c = tl.get();
         tl.set(initConn);
         return c;
     }
     
}

  這樣子,都是用同一個連接,但是每個連接都是新的,是同一個連接的副本。

  那么實現機制是如何的呢?

  1、每個Thread對象內部都維護了一個ThreadLocalMap這樣一個ThreadLocal的Map,可以存放若干個ThreadLocal。

1
2
3
/* ThreadLocal values pertaining to this thread. This map is maintained
  * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null ;

  2、當我們在調用get()方法的時候,先獲取當前線程,然后獲取到當前線程的ThreadLocalMap對象,如果非空,那么取出ThreadLocal的value,否則進行初始化,初始化就是將initialValue的值set到ThreadLocal中。

1
2
3
4
5
6
7
8
9
10
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();
}

  3、當我們調用set()方法的時候,很常規,就是將值設置進ThreadLocal中。

  4、總結:當我們調用get方法的時候,其實每個當前線程中都有一個ThreadLocal。每次獲取或者設置都是對該ThreadLocal進行的操作,是與其他線程分開的。

  5、應用場景:當很多線程需要多次使用同一個對象,並且需要該對象具有相同初始化值的時候最適合使用ThreadLocal。

  6、其實說再多也不如看一下源碼來得清晰。如果要看源碼,其中涉及到一個WeakReference和一個Map,這兩個地方需要了解下,這兩 個東西分別是a.Java的弱引用,也就是GC的時候會銷毀該引用所包裹(引用)的對象,這個threadLocal作為key可能被銷毀,但是只要我們 定義成他的類不卸載,tl這個強引用就始終引用着這個ThreadLocal的,永遠不會被gc掉。b.和HashMap差不多。

  事實上,從本質來講,就是每個線程都維護了一個map,而這個map的key就是threadLocal,而值就是我們set的那個值,每次線 程在get的時候,都從自己的變量中取值,既然從自己的變量中取值,那肯定就不存在線程安全問題,總體來講,ThreadLocal這個變量的狀態根本沒 有發生變化,他僅僅是充當一個key的角色,另外提供給每一個線程一個初始值。如果允許的話,我們自己就能實現一個這樣的功能,只不過恰好JDK就已經幫 我們做了這個事情。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM