ThreadLocal是什麼
ThreadLocal,顧名思義,它不是一個線程,而是線程的一個本地化對象。當工作于多線程中的對象使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程配置設定一個獨立的變量副本。是以每一個線程都可以獨立地改變自己的副本,而不會影響其他線程所對應的副本。從線程的角度看,這個變量就像是線程的本地變量,這也是類名中“Local”所要表達的意思。
ThreadLocal的方法很簡單,主要的就是4個方法
1
2
3
4
5
6
7
8
9
10
11
| // 設定目前線程的線程局部變量的值
void set(T value)
// 傳回目前線程所對應的線程局部變量
public T get()
// 将目前線程局部變量的值删除,目的是為了減少記憶體的占用,加快記憶體回收的速度
public void remove()
// 傳回該線程局部變量的初始值,讓子類繼承而設計的。這個方法是一個延遲調用方法,線上程第一次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的預設實作直接傳回一個null。
protected T initialValue()
|
ThreadLocal的實作思路
那麼ThreadLocal是如何做到為每一個線程維護一份獨立的變量副本呢?其實實作思路很簡單:在ThreadLocal類中有一個Map,用于存儲沒一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。我們自己也可以提供一個簡單的實作版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| public class SimpleThreadLocal {
private Map<Thread, Object> valueMap = Collections.synchronizedMap(new HashMap<>());
public void set(Object newValue) {
valueMap.set(Thread.currentThread(), newValue);
}
public Object get() {
Thread currentThread = Thread.currentThread();
Object o = valueMap.get(currentThread);
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的運用
下面是我在項目中運用ThreadLocal來實作多線程情況下,使用者資訊的管理Session:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| package com.benjamin.common.session;
import java.util.HashMap;
import java.util.Map;
/**
* Created by piqiu on 2/27/16.
* 使用者Session資訊保管類
*/
public class UserSession {
/** 儲存使用者的資訊 **/
private static final ThreadLocal<Map<Object, Object>> userThreadLocal = new ThreadLocal<>();
protected UserSession(){}
public static Object get(String attribute) {
Map<Object, Object> userInfo = userThreadLocal.get();
Object o = null;
if (userInfo != null) {
o = userInfo.get(attribute);
}
return o;
}
public static <T> T get(String attribute, Class<T> clazz) {
return (T)get(attribute);
}
public static void set(Object attribute, Object value) {
Map<Object, Object> userInfo = userThreadLocal.get();
if (userInfo == null) {
userInfo = new HashMap<>();
userThreadLocal.set(userInfo);
}
userInfo.put(attribute, value);
}
}
|
ThreadLocal與Thread同步機制的比較
ThreadLocal和線程同步機制都是為了解決多線程中相同變量的通路沖突問題,那麼,ThreadLocal和線程同步機制相比有什麼優勢呢?
在同步機制中,通過對象的鎖機制保證同一時間隻有一個線程通路變量。這時該變量是多個線程共享的,使用同步機制要求程式缜密的分析什麼時候對變量進行讀寫,什麼時候需要鎖定某個對象,什麼時候釋放對象鎖等繁雜的問題,程式設計和編寫難度相對較大
而ThreadLocal則從另一個角度來解決多線程的并發通路。ThreadLocal為每一個線程提供一個獨特的變量副本,進而隔離了多個線程對通路資料的沖突。因為每一個線程都擁有自己的變量副本,進而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的對象封裝,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。
概括起來說,對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式:通路串行化,對象共享化。而ThreadLocal采用了“以空間換時間”的方式:通路并行化,對象獨享化。前者僅提供一份變量,讓不同的線程排隊通路,而後者為每一個線程都提供了一份變量,是以可以同時通路而互不影響。
Spring利用ThreadLocal解決線程安全問題
我們知道在一般情況下,隻有無狀态的Bean才可以在多線程環境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用域。就是因為Spring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder)中非線程安全的“狀态性對象”采用ThreadLocal進行封裝,讓它們也成為線程安全的“狀态性對象”,因為有狀态的Bean就能夠以singleton方式在多線程中正常工作了。
通過檢視TransactionSynchronizationManager源碼可以發現Spring事務的工作機制,我們可以自己利用ThreadLocal來實作自定義的Session和Transaction管理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
| package com.benjamin.common;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
/**
* Created by piqiu on 2/27/16.
*/
public class HibernateUtil {
private static final Logger logger = LoggerFactory.getLogger(HibernateUtil.class);
/** 存儲hibernate session的ThreadLocal **/
private static final ThreadLocal<Session> sessionThreadLocal = new ThreadLocal<>();
/** 存儲事務n的ThreadLocal **/
private static final ThreadLocal<Transaction> transactionThreadLocal = new ThreadLocal<>();
/** session工廠 **/
private static SessionFactory sessionFactory;
/**
* 初始化sessionFactory
* @param file
*/
public static void initSessionFactory(File file) {
Configuration configuration = new Configuration();
configuration.configure(file);
sessionFactory = configuration.buildSessionFactory();
}
/**
* 取得目前線程綁定的session
* @return
*/
public static Session currentSession() {
Session session = sessionThreadLocal.get();
if (session == null) {
session = sessionFactory.openSession();
sessionThreadLocal.set(session);
}
return session;
}
/**
* 關閉session
*/
public static void closeSession() {
Session session = sessionThreadLocal.get();
if (session != null) {
sessionThreadLocal.set(null);
session.clear();
session.close();
}
}
/**
* 取得目前session的事務
* @return
*/
public static Transaction transaction() {
Transaction transaction = transactionThreadLocal.get();
if (transaction == null) {
transaction = currentSession().beginTransaction();
transactionThreadLocal.set(transaction);
}
return transaction;
}
/**
* 送出事務
*/
public static void commitTransaction() {
Transaction transaction = transactionThreadLocal.get();
transactionThreadLocal.set(null);
if (transaction != null) {
transaction.commit();
}
}
/**
* 復原事務
*/
public static void rollbackTransaction() {
Transaction transaction = transactionThreadLocal.get();
transactionThreadLocal.set(null);
if (transaction != null) {
transaction.rollback();
}
} |