ThreadLocal是Java中的線程局部變量,用於存放線程的局部變量。java
ThreadLocal爲每一個線程的中併發訪問的數據提供一個副本,經過訪問副原本運行業務,這樣的結果是耗費了內存,可是確避免線程同步所帶來性能消耗,也減小了線程併發控制的複雜度。mysql
首先看一下ThreadLocal的API:sql
能夠看出ThreadLocal內部應該就是封裝了一個Map,本身實現ThreadLocal:數據庫
public class SimpleThreadLocal { private Map valueMap = Collections.synchronizedMap(new HashMap()); public void set(Object newValue) { //①鍵爲線程對象,值爲本線程的變量副本 valueMap.put(Thread.currentThread(), newValue); } public Object get() { Thread currentThread = Thread.currentThread(); //②返回本線程對應的變量 Object o = valueMap.get(currentThread); //③若是在Map中不存在,放到Map中保存起來 if (o == null && !valueMap.containsKey(currentThread)) { o = initialValue(); valueMap.put(currentThread, o); } return o; } public void remove() { valueMap.remove(Thread.currentThread()); } public Object initialValue() { return null; } }
以上代碼很好理解,JDK中的實現比這複雜,能夠自行查看源碼。安全
ThreadLocal對象一般用於防止對可變的單實例變量或全局變量進行共享。服務器
當一個類中使用了static成員變量的時候,必定要多問問本身,這個static成員變量須要考慮線程安全嗎?也就是說,多個線程須要獨享本身的static成員變量嗎?若是須要考慮,不妨使用ThreadLocal。多線程
例如,在單線程應用程序中可能會維護一個全局的數據庫鏈接,並在程序啓動的時候初始化這個鏈接,從而避免在調用每一個方法的時候都要傳遞一個Connection對象。因爲JDBC的鏈接對象不必定時線程安全的,所以,當多線程應用程序在沒有協同的狀況下使用全局變量時,就是否是線程安全的。經過把JDBC的鏈接保存到ThreadLocal對象中,每一個線程都會擁有本身的鏈接。架構
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionManager { private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { @Override protected Connection initialValue() { Connection conn = null; try { conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/test", "username", "password"); } catch (SQLException e) { e.printStackTrace(); } return conn; } }; public static Connection getConnection() { return connectionHolder.get(); } public static void setConnection(Connection conn) { connectionHolder.set(conn); } }
只要使用了「池」(線程池、鏈接池),在使用ThreadLocal時,尤爲須要注意,每一個線程在使用ThreadLocal的時候,必須對ThreadLocal執行一次clear操做,避免出現線程污染問題。併發
線程池中的線程是重複利用的,只要線程還在,ThreadLocal線程本地變量會一直存在系統中,在JavaEE的服務器中尤其明顯。ide
引用高廣超在Java解讀-ThreadLocal詳解與應用中所說:
根據池中的線程數量(在運行環境中大於100個線程是正常的)以及ThreadLocal變量中對象的大小,可能會發生致命的內存問題。例如對線程池中的200個線程進行配置以及將ThreadLocal變量的大小設置爲5MB,這將會致使有1GB的堆空間被這些變量所佔用。這將會致使一個GC的開銷而且可能會因爲OutOfMemoryError致使JVM崩潰。
參考資料: