概念:
ThreadLocal并不是一個Thread芍瑞,而是一個線程內(nèi)部的存儲類彬坏,可以在指定線程內(nèi)存儲數(shù)據(jù),數(shù)據(jù)存儲以后叉信,只有指定線程可以得到存儲數(shù)據(jù)。
ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路
原理:
當使用ThreadLocal維護變量時艘希,ThreadLocal為每個使用該變量的線程提供獨立的變量副本硼身,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本覆享。
源碼分析:
- Implements a thread-local storage, that is, a variable for which each thread
- has its own value. All threads share the same {@code ThreadLocal} object,
- but each sees a different value when accessing it, and changes made by one
- thread do not affect the other threads. The implementation supports
*實現(xiàn)一個線程本地存儲,每個線程的一個變量
*有它自己的價值佳遂。所有線程共享相同的ThreadLocal對象,
*但每看到一個不同的價值當訪問它,和更改
*線程不會影響其他線程
public class ThreadLocal<T> {
可以看出threadlocal是一個范型類,這標志著threadlocal可以存儲所有數(shù)據(jù)
set()方法:
public void set(T value) {
Thread currentThread = Thread.currentThread();//獲取當前運行的線程
Values values = values(currentThread);//返回一個存儲類
if (values == null) {
values = initializeValues(currentThread);//初始化return new Values()
}
values.put(this, value);//插入
}
首先會獲取當前線程撒顿,根據(jù)當前線程獲取Values存儲類丑罪,再調(diào)用values存儲類中的put方法,將內(nèi)容存儲到Values內(nèi)部類的table數(shù)組的下標key.reference中。
values.put():
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
把values的值傳入到一個table數(shù)組的key.reference的下一個下標中
table就以key吩屹,value的形式存儲了線程的本地變量跪另,偶數(shù)位放key,基數(shù)位放value煤搜。
兩個地方不太清楚
1.key.reference 是什么值
2.for (int index = key.hash & mask;; index = next(index)) 條件是什么免绿?
get():
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
方法返回一個當前線程的當前value值,如果這個值沒有初始化擦盾,那么會通過initialValue();返回一個null嘲驾。
remove():
void remove(ThreadLocal<?> key) {
cleanUp();
for (int index = key.hash & mask;; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
// Success!
table[index] = TOMBSTONE;
table[index + 1] = null;
tombstones++;
size--;
return;
}
if (reference == null) {
// No entry found.
return;
}
}
}
內(nèi)存釋放,手動釋放當前線程的存儲的值迹卢。
ThreadLocal和線程同步機制相比有什么優(yōu)勢呢辽故?
對于同步機制中來說,通過對象的鎖機制保證同一時間只有一個線程訪問變量腐碱。這時該變量是多個線程共享的誊垢,使用同步機制要求程序慎密地分析什么時候?qū)ψ兞窟M行讀寫,什么時候需要鎖定某個對象喻杈,什么時候釋放對象鎖等繁雜的問題彤枢,程序設(shè)計和編寫難度相對較大狰晚。
對于ThreadLocal來說筒饰,它為每一個線程提供一個獨立的變量副本
,從而隔離了多個線 程對數(shù)據(jù)的訪問沖突壁晒。因為每一個線程都擁有自己的變量副本瓷们,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象秒咐,在編 寫多線程代碼時谬晕,可以把不安全的變量封裝進ThreadLocal,從另一個角度來解決多線程的并發(fā)訪問携取。