Java併發基礎--ThreadLocal

1、ThreadLocal定義

ThreadLocal是一個能夠提供線程局部變量的類,ThreadLocal爲解決多線程程序的併發問題提供了一種新的思路,經過爲每一個線程提供一個獨立的變量副本解決了變量併發訪問的衝突問題。在不少狀況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結果程序擁有更高的併發性。redis

ThreadLocal的做用是提供線程內的局部變量,這種變量在線程的生命週期內起做用。做用:提供一個線程內公共變量(好比本次請求的用戶信息),減小同一個線程內多個函數或者組件之間一些公共變量的傳遞的複雜度,或者爲線程提供一個私有的變量副本,這樣每個線程均可以隨意修改本身的變量副本,而不會對其餘線程產生影響。數據庫

2、基本使用

 1 public class ThreadLocalDemo {  2     public static ThreadLocal<Person> local = new ThreadLocal<Person>(){  3         protected Person initialValue() {  4             return new Person(Thread.currentThread().getName());  5  };  6  };  7     public static Person getPerson(){  8         return local.get();  9  } 10     
11     public static void main(String[] args) { 12         for(int i=0;i<5;i++){ 13             new Thread(new Runnable() { 14  @Override 15                 public void run() { 16                     System.out.println(Thread.currentThread().getName()+":"+ThreadLocalDemo.getPerson().getName()); 17  } 18  }).start(); 19  } 20         System.out.println(Thread.currentThread().getName()+":"+ThreadLocalDemo.getPerson().getName()); 21  } 22 
23 } 24 
25 class Person{ 26     private String name; 27     
28     public Person(String name){ 29         this.name = name; 30  } 31     public String getName() { 32         return name; 33  } 34 
35     public void setName(String name) { 36         this.name = name; 37  } 38     
39 }

結果輸出:安全

main:main Thread-0:Thread-0 Thread-2:Thread-2 Thread-4:Thread-4 Thread-1:Thread-1 Thread-3:Thread-3

從結果能夠看出,local對象針對不一樣的線程提供的Person變量是不一樣的。而且互不影響。同一個線程可以共享local中保存的對象。session

3、源碼分析

1.get()方法,用來獲取當前調用get方法的線程對應在ThreadLocal中保存的變量的副本。多線程

public T get() { Thread t = Thread.currentThread();//獲取當前線程
        ThreadLocalMap map = getMap(t);//經過線程獲取對應的ThreadLocalMap,ThreadLocalMap是ThreadLocal中的一個靜態內部類,可將他看作一個特殊的map,key是ThreadLocal,value則是保存的變量的值。
        if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }

經過線程t獲取,在Thread類中定義了ThreadLocal.ThreadLocalMap threadLocals = null;即將線程和ThreadLocalMap聯繫起來,初始時,threadLocals=null,則須要經過setInitialValue方法建立,具體以下:併發

private T setInitialValue() { T value = initialValue();//調用ThreadLocal中的initialValue方法獲取變量的初始值
        Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);//當線程關聯的ThreadLocalMap爲null的時候,建立一個ThreadLocalMap,並賦值讓線程t的threadLocals引用指向新建立的ThreadLocalMap對象。
        return value; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }

經過ThreadLocal的源碼分析,能夠知道每一個線程都有一個ThreadLocalMap類型的引用threadLocals,線程初始時候爲null,當咱們調用set或者get方法的時候,經過當前線程獲取ThreadLocalMap,若是不爲null,則經過當前threadLocal做爲key,獲取對應的value值。若是爲null,則new ThreadLocalMap(this, firstValue);,this指向ThreadLocal對象,firstValue則是initialValue()方法中指定的初始值。ide

2.set()方法函數

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } //經過線程類Thread獲取關聯的ThreadLocalMap對象,若是不爲null,直接調用ThreadLocalMap的set方法,設置鍵值對,key爲ThreadLocal對象,value則爲設置的值。

3.remove()方法源碼分析

public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }

4、應用場景

數據庫鏈接,session管理,redis鏈接等。例如經典的使用,以下所示:this

 1 public class JdbcTemplateImpl {  2     // 數據庫用戶名
 3     private String username;  4     // 數據庫密碼
 5     private String password;  6     // 驅動信息
 7     private String driver;  8     // 數據庫地址
 9     private String url; 10     // 聲明線程共享變量
11     public static ThreadLocal<Connection>    connections= new ThreadLocal<Connection>(); 12     
13     public JdbcTemplateImpl(String driver, String url, String username, String password) { 14         this.driver = driver; 15         this.url = url; 16         this.username = username; 17         this.password = password; 18         try { 19             Class.forName(this.driver); 20         } catch (Exception e) { 21  e.printStackTrace(); 22  } 23  } 24     
25     public Connection getConnection() { 26         Connection connection = connections.get(); 27         try { 28             if (connection != null && !connection.isClosed()) { 29                 return connection; 30             }else { 31                 connection = DriverManager.getConnection(this.url, this.username, this.password); 32  connections.set(connection); 33  } 34         } catch (SQLException e) { 35  e.printStackTrace(); 36  } 37         return connection; 38  } 39 }
相關文章
相關標籤/搜索