天天看點

threadlocal存連接配接對象的目的_淺析​ThreadLocal

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的基本原理,我們可以提供一個基本模拟版本給大家參考。

publicclassSimpleThreadLocal {

privateMap valueMap = Collections.synchronizedMap(newHashMap());

publicvoidset(Object newValue) {

//鍵為線程對象,值為本線程的變量副本

valueMap.put(Thread.currentThread(), newValue);

}

publicObject get() {

Thread currentThread = Thread.currentThread();

//傳回本線程對應的變量

Object o = valueMap.get(currentThread);

//如果在Map中不存在,放到Map中儲存起來

if(o == null&& !valueMap.containsKey(currentThread)) {

o = initialValue();

valueMap.put(currentThread, o);

}

returno;

}

publicvoidremove() {

valueMap.remove(Thread.currentThread());

}

publicObject initialValue() {

returnnull;

}

}

SimpleThreadLocal 一共提供了4個方法

void set(Object newValue):根據目前線程,來設定目前線程的對象值,

Object get():擷取目前線程的對象

void remove():删除目前線程裡的對象

initialValue():設定目前對象的預設初始值

ThreadLocal源碼

以下是JDK1.5中的TheadLocal部分源碼:

threadlocal存連接配接對象的目的_淺析​ThreadLocal

可以看到,源碼中的方法與我們上述模拟的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類中定義。

threadlocal存連接配接對象的目的_淺析​ThreadLocal

從上可以看到,每一個線程的執行個體的都有自己的成員變量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使用步驟

threadlocal存連接配接對象的目的_淺析​ThreadLocal

1、在多線程的類(如ThreadDemo類)中,建立一個ThreadLocal對象threadXxx,用來儲存線程間需要隔離處理的對象xxx。

2、在ThreadDemo類中,建立一個擷取要隔離通路的資料的方法getXxx(),在方法中判斷,若ThreadLocal對象為null時候,應該new()一個隔離通路類型的對象,并強制轉換為要應用的類型。

3、在ThreadDemo類的run()方法中,通過getXxx()方法擷取要操作的資料,這樣可以保證每個線程對應一個資料對象,在任何時刻都操作的是這個對象。

threadlocal存連接配接對象的目的_淺析​ThreadLocal

本文作者:易泉梁(點融黑幫),耿直程式猿一枚,會寫點代碼,喜歡享受生活,上不了廳堂,下得了廚房,喜歡運動,排球(二傳),羽毛球,乒乓球水準可以虐菜鳥.人其實是可以快樂地生活的,隻是我們自己選擇了複雜,選擇了歎息!

threadlocal存連接配接對象的目的_淺析​ThreadLocal