3C科技 娛樂遊戲 美食旅遊 時尚美妝 親子育兒 生活休閒 金融理財 健康運動 寰宇綜合

Zi 字媒體

2017-07-25T20:27:27+00:00
加入好友
ThreadLocal定義項目中,如果某個對象是線程不安全的,在多線程環境下,對對象必須要採用synchronized加鎖來進行線程同步。但是這樣來解決線程安全的問題,會帶來很大的性能損失。如果在獲取資料庫connection對象時,使用加鎖來解決各個connection對象之間的線程安全問題。那麼用戶訪問時,一個線程在使用資料庫操作,而其他線程的connection對象只能串列等待,這樣會嚴重影響性能問題。為了更好地處理解決這種問題,java中提供了java.lang.ThreadLocal這個類來實現多線程訪問對象的解決方案。ThreadLocal的主要應用場景為按線程多實例(每個線程對應一個實例)的對象的訪問,並且這個對象很多地方都要用到。ThreadLocal官網解釋:This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g.,a user ID or Transaction ID).ThreadLocal類用來提供線程內部的局部變數。這些變數在多線程環境下訪問(通過get或set方法訪問)時能保證各個線程里的變數相對獨立於其他線程內的變數,ThreadLocal實例通常來說都是private static類型。 ThreadLocal不是為了解決多線程訪問共享變數,而是為每個線程創建一個單獨的變數副本,提供了保持對象的方法和避免參數傳遞的複雜性。在Spring,Hibernate,Struts2中對於ThreadLocal都有廣泛的應用,在Hibernate中獲取connection也是通過ThreadLocal來封裝connection對象,實現了連接connection之間不會相互影響的效果。在Struts2中通過ThreadLocal來封裝每個請求,讓每個用戶的request請求分離達到不同用戶線程之間獲取不同的request對象效果。ThreadLocal原理ThreadLocal可以看做是一個容器,容器裡面存放著屬於當前線程的變數。使用一個Map來把線程作為key,對象作為value來存儲對象,這樣能上達到各個線程能夠存儲一個對象,讓每個線程的對象獨立分割開來的效果。對於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; } } ThreadLocal源碼以下是JDK1.5中的TheadLocal部分源碼:可以看到,源碼中的方法與我們上述模擬的ThreadLocal簡單版本一致。而在方法內部做了一些更加複雜的控制。1.首先從類的成員變數有3個int類型的變數。其中threadLocalHashCode是類的實例變數,nextHashCode表示了下一個ThreadLocal實例的threadLocalHashCode的值,而HASH_INCREMENT是一個常量,表示每次threadLocalHashCode的增量。threadLocalHashCode調用了nextHashCode這個方法。這個方法的目的是將下一個hashCode的值賦值給了threadLocalHashCode,然後nextHashCode進行自增。類的實例變數會在類每次實例化的時候初始化,所以每次實例化ThreadLocal的時候,它的threadLocalHashCode都會自增。ThreadLocal是作為一個工具拿來給不同的對象來使用,為了區分不同的ThreadLocal的實例,所以需要定義threadLocalHashCode來區別不同的ThreadLocal實例。2.在上面代碼中,我們可以看到有一個ThreadLocalMap的類,這個類是ThreadLocal的內部類,受篇幅限制,並沒有截圖出來。這個內部類的實例卻在Thread類中定義。從上可以看到,每一個線程的實例的都有自己的成員變數threadLocals,也就是說是threadLocals這個對象依賴於每個線程存在。當調用set方法時,如果當前線程的threadLocals不為空,就把當前ThreadLocal實例做為key,要保持的對象做為值,設置到當前線程的ThreadLocalMap中;如果沒有threadLocals,就創建並且設置。調用get方法的時候,從當前ThreadLocal對象和當前線程,取得對應的緩存對象。ThreadLocal類通過操作每一個線程特有的ThreadLocalMap對象,從而實現了變數訪問在不同線程中的隔離。因為每個線程的變數都是自己特有的,完全不會有併發錯誤。在JDK1.5之後,放在ThreadLocal中的對象,都是泛型,這樣可以避免使用Object類型的強制類型轉化。ThreadLocal在Hibernate中的使用以下為Hibernate中使用ThreadLocal的範例,我們可以學習一下public class HibernateUtil {private static Log log = LogFactory.getLog(HibernateUtil.class); //定義SessionFactory private static final SessionFactory sessionFactory; static { try { // 通過默認配置文件Hibernate.cfg.xml創建SessionFactory sessionFactory = new Configuration.configure.buildSessionFactory; } catch (Throwable ex) { log.error("初始化SessionFactory失敗!", ex); throw new ExceptionInInitializerError(ex); } } //創建線程局部變數session,用來保存Hibernate的Session public static final ThreadLocal session = new ThreadLocal; //獲取當前線程中的Session public static Session currentSession throws HibernateException { Session s = (Session) session.get; // 如果Session還沒有打開,則新開一個Session if (s == null) { s = sessionFactory.openSession; session.set(s); //將新開的Session保存到線程局部變數中 } return s; } public static void closeSession throws HibernateException { //獲取線程局部變數,並強制轉換為Session類型 Session s = (Session) session.get; session.set(null); if (s != null) s.close; } }示例是在開啟資料庫連接的session對象的過程。其實,所有類似於這種緩存對象的代碼都一樣。首先從ThreadLocal中取當前線程的對象,如果對象為null,就重新創建對象,並把創建好的對象放到ThreadLocal裡面。ThreadLocal持有的對象能夠保持各個線程之間對象的獨立。ThreadLocal使用步驟1、在多線程的類(如ThreadDemo類)中,創建一個ThreadLocal對象threadXxx,用來保存線程間需要隔離處理的對象xxx。2、在ThreadDemo類中,創建一個獲取要隔離訪問的數據的方法getXxx,在方法中判斷,若ThreadLocal對象為null時候,應該new一個隔離訪問類型的對象,並強制轉換為要應用的類型。3、在ThreadDemo類的run方法中,通過getXxx方法獲取要操作的數據,這樣可以保證每個線程對應一個數據對象,在任何時刻都操作的是這個對象。本文作者:易泉梁(點融黑幫),耿直程序猿一枚,會寫點代碼,喜歡享受生活,上不了廳堂,下得了廚房,喜歡運動,排球(二傳),羽毛球,乒乓球水平可以虐菜鳥.人其實是可以快樂地生活的,只是我們自己選擇了複雜,選擇了嘆息!

本文由yidianzixun提供 原文連結

寫了 5860316篇文章,獲得 23313次喜歡
精彩推薦