您好,登錄后才能下訂單哦!
Java中ThreadLocal是什么?這個問題可能是我們日常學習或工作經常見到的。希望通過這個問題能讓你收獲頗深。下面是小編給大家帶來的參考內容,讓我們一起來看看吧!
ThreadLocal基本在項目開發中基本不會用到, 但是面試官是比較喜歡問這類問題的;所以還是有必要了解一下該類的功能與原理的.
ThreadLocal是什么
ThreadLocal是一個將在多線程中為每一個線程創建單獨的變量副本的類; 當使用ThreadLocal來維護變量時, ThreadLocal會為每個線程創建單獨的變量副本, 避免因多線程操作共享變量而導致的數據不一致的情況;
一般來說, ThreadLocal
在實際工業生產中并不常見, 但是在很多框架中使用卻能夠解決一些框架問題; 比如Spring中的事務、Spring 中 作用域 Scope
為 Request的Bean
使用ThreadLocal來解決.
1、將需要被多線程訪問的屬性使用ThreadLocal變量來定義; 下面以網上多數舉例的DBConnectionFactory類為例來舉例
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBConnectionFactory { private static final ThreadLocal<Connection> dbConnectionLocal = new ThreadLocal<Connection>() { @Override protected Connection initialValue() { try { return DriverManager.getConnection("", "", ""); } catch (SQLException e) { e.printStackTrace(); } return null; } }; public Connection getConnection() { return dbConnectionLocal.get(); } }
這樣在Client獲取Connection的時候, 每個線程獲取到的Connection都是該線程獨有的, 做到Connection的線程隔離; 所以并不存在線程安全問題
1、主要是用到了Thread對象中的一個ThreadLocalMap類型的變量threadLocals, 負責存儲當前線程的關于Connection的對象, 以dbConnectionLocal 這個變量為Key, 以新建的Connection對象為Value; 這樣的話, 線程第一次讀取的時候如果不存在就會調用ThreadLocal的initialValue方法創建一個Connection對象并且返回;
具體關于為線程分配變量副本的代碼如下:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
1、首先獲取當前線程對象t, 然后從線程t中獲取到ThreadLocalMap的成員屬性threadLocals
2、如果當前線程的threadLocals已經初始化(即不為null) 并且存在以當前ThreadLocal對象為Key的值, 則直接返回當前線程要獲取的對象(本例中為Connection);
3、如果當前線程的threadLocals已經初始化(即不為null)但是不存在以當前ThreadLocal對象為Key的的對象, 那么重新創建一個Connection對象, 并且添加到當前線程的threadLocals Map中,并返回
4、如果當前線程的threadLocals屬性還沒有被初始化, 則重新創建一個ThreadLocalMap對象, 并且創建一個Connection對象并添加到ThreadLocalMap對象中并返回。
如果存在則直接返回很好理解, 那么對于如何初始化的代碼又是怎樣的呢?
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
1、首先調用我們上面寫的重載過后的initialValue方法, 產生一個Connection對象
2、繼續查看當前線程的threadLocals是不是空的, 如果ThreadLocalMap已被初始化, 那么直接將產生的對象添加到ThreadLocalMap中, 如果沒有初始化, 則創建并添加對象到其中;
同時, ThreadLocal還提供了直接操作Thread對象中的threadLocals的方法
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
這樣我們也可以不實現initialValue, 將初始化工作放到DBConnectionFactory的getConnection方法中:
public Connection getConnection() { Connection connection = dbConnectionLocal.get(); if (connection == null) { try { connection = DriverManager.getConnection("", "", ""); dbConnectionLocal.set(connection); } catch (SQLException e) { e.printStackTrace(); } } return connection; }
那么我們看過代碼之后就很清晰的知道了為什么ThreadLocal能夠實現變量的多線程隔離了; 其實就是用了Map的數據結構給當前線程緩存了, 要使用的時候就從本線程的threadLocals對象中獲取就可以了, key就是當前線程;
當然了在當前線程下獲取當前線程里面的Map里面的對象并操作肯定沒有線程并發問題了, 當然能做到變量的線程間隔離了;
現在我們知道了ThreadLocal到底是什么了, 又知道了如何使用ThreadLocal以及其基本實現原理了是不是就可以結束了呢? 其實還有一個問題就是ThreadLocalMap是個什么對象, 為什么要用這個對象呢?
本質上來講, 它就是一個Map, 但是這個ThreadLocalMap與我們平時見到的Map有點不一樣
1、它沒有實現Map接口;
2、它沒有public的方法, 最多有一個default的構造方法, 因為這個ThreadLocalMap的方法僅僅在ThreadLocal類中調用, 屬于靜態內部類
3、ThreadLocalMap的Entry實現繼承了WeakReference<ThreadLocal<?>>
4、該方法僅僅用了一個Entry數組來存儲Key, Value; Entry并不是鏈表形式, 而是每個bucket里面僅僅放一個Entry;
感謝各位的閱讀!看完上述內容,你們對Java中ThreadLocal是什么大概了解了嗎?希望文章內容對大家有所幫助。如果想了解更多相關文章內容,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。