ThreadLocal和線程同步機(jī)制相比:都是為了解決多線程中相同變量的訪問沖突問題呼伸。
在同步機(jī)制中恐锦,通過對象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問變量椿浓。這時(shí)該變量是多個(gè)線程共享的凑兰。
ThreadLocal會為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本底洗,從而隔離了多個(gè)線程對數(shù)據(jù)的訪問沖突款筑。ThreadLocal提供了線程安全的共享相同名稱的不同對象智蝠。
在一個(gè)村中有100戶人家,當(dāng)大家要使用村里唯一的一臺拖拉機(jī)時(shí)奈梳,需要使用同步機(jī)制杈湾。當(dāng)每家騎自家的自行車時(shí),使用ThreadLocal攘须,雖然大家騎的都是自行車漆撞,但是是不同的自行車實(shí)例。
package com.hfbank.biz.service;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
/**
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
{@code null} values.
@see java.lang.Thread
@author Bob Lee
jxy 為了更好的使用于宙,1.5增加了泛型的支持浮驳,可以看出為了能夠更好更簡單的被使用,不放過需要優(yōu)化的任何地方
Values中為什么不把:private Object[] table;設(shè)置為泛型捞魁?
-
一個(gè)ThreadLocal只能保持一個(gè)Object至会,如果想保持多個(gè)應(yīng)如何處理?
*/
public class ThreadLocal<T> {/* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
// jxy 已經(jīng)多次看到此類注釋谱俭,代碼如人生/**
- Creates a new thread-local variable.
*/
public ThreadLocal() {}
/**
Returns the value of this variable for the current thread. If an entry
doesn't yet exist for this variable on this thread, this method will
create an entry, populating the value with the result of
{@link #initialValue()}.
-
@return the current value of the variable for the calling thread.
/
@SuppressWarnings("unchecked")
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
/*- jxy values是需要和Thread類結(jié)合使用的奉件,并且是一個(gè)類似map的存儲類
- 在Thread類中有: java.lang.ThreadLocal.Values localValues;
- localValues在Thread類中只是定義為包內(nèi)可見宵蛀,但沒有任何操作,就是為了給ThreadLocal類使用
- Thread類的實(shí)例在運(yùn)行時(shí)县貌,每個(gè)線程都是不同的實(shí)例术陶,這樣localValues也就不會相互干擾
- 這樣兩個(gè)類配合實(shí)現(xiàn)不同線程獲取不同實(shí)例的需求。
- 如果不和Thread類配合能否實(shí)現(xiàn)現(xiàn)有功能煤痕?
- values為什么做成可以存儲多個(gè)key-value對梧宫?
*/
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);
}
/**
- Provides the initial value of this variable for the current thread.
- The default implementation returns {@code null}.
- @return the initial value of the variable.
- jxy 該函數(shù)是protected類型的,很顯然是建議在子類重載該函數(shù)的摆碉,所以通常該函數(shù)都會以匿名內(nèi)部類的形式被重載塘匣,以指定初始值
*/
protected T initialValue() {
return null;
}
/**
- Sets the value of this variable for the current thread. If set to
- {@code null}, the value will be set to null and the underlying entry will
- still be present.
- @param value the new value of the variable for the caller thread.
*/
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
/**
- Removes the entry for this variable in the current thread. If this call
- is followed by a {@link #get()} before a {@link #set},
- {@code #get()} will call {@link #initialValue()} and create a new
- entry with the resulting value.
- @since 1.5
*/
public void remove() {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
values.remove(this);
}
}
/**
- Creates Values instance for this thread and variable type.
*/
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
/**
- Gets Values instance for this thread and variable type.
*/
Values values(Thread current) {
return current.localValues;
}
/** Weak reference to this thread local instance. */
// jxy 此處使用WeakReference可以使得當(dāng)ThreadLocal可以被回收,而不會因?yàn)樵趘alues中保存有引用而無法回收
private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this);/** Hash counter. */
private static AtomicInteger hashCounter = new AtomicInteger(0);/**
- Internal hash. We deliberately don't bother with #hashCode().
- Hashes must be even. This ensures that the result of
- (hash & (table.length - 1)) points to a key and not a value.
- We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
- every other bucket) to help prevent clustering.
- jxy 通過定義一個(gè)static的AtomicInteger類型變量hashCounter兆解,
- 實(shí)現(xiàn)每一個(gè)ThreadLocal類實(shí)例的hash值不相同馆铁。即每次比上一次的值加1.
*/
private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
/**
-
Per-thread map of ThreadLocal instances to values.
*/
static class Values {/**
- Size must always be a power of 2. 2 的冪次方
- jxy 為什么必須是2的冪次方?
- 在進(jìn)行hash計(jì)算的時(shí)候锅睛,是使用:int index = hash & values.mask;
mask = table.length - 1;
table = new Object[capacity * 2];
- 只有當(dāng)INITIAL_SIZE為2的冪次方時(shí)埠巨,mask才可以是類似:00001111的值,在擴(kuò)充table時(shí)也是通過*2來計(jì)算capacity的
- hash每次的值不一樣现拒,這樣計(jì)算出的index(即取hash最后幾位的值)就是唯一的辣垒。
*/
private static final int INITIAL_SIZE = 16;
/**
- Placeholder for deleted entries.
- jxy 為什么要定義tombstone?
- 因?yàn)橐粋€(gè)線程中可以定義多個(gè)ThreadLocal印蔬,但是values中的table并不是在定義ThreadLocal時(shí)校驗(yàn)是否需要擴(kuò)充勋桶,
- 當(dāng)定義量大于capacity時(shí),此時(shí)index = hash & values.mask有可能得到重復(fù)的值侥猬,所以要跟蹤哪些entry已經(jīng)廢棄
- 在put時(shí)需要進(jìn)行tombstone的判斷處理例驹。
*/
private static final Object TOMBSTONE = new Object();
/**
- Map entries. Contains alternating keys (ThreadLocal) and values.
- The length is always a power of 2.
- jxy table中每一個(gè)entry是有三個(gè)狀態(tài)的,null沒有進(jìn)行過設(shè)置退唠,tombstone鹃锈,設(shè)置過但是現(xiàn)在廢棄了,正常設(shè)置狀態(tài)
*/
private Object[] table;
/** Used to turn hashes into indices. */
private int mask;/** Number of live entries. */
private int size;/** Number of tombstones. */
private int tombstones;/** Maximum number of live entries and tombstones. */
private int maximumLoad;/** Points to the next cell to clean up. */
private int clean;/**
- Constructs a new, empty instance.
*/
Values() {
initializeTable(INITIAL_SIZE);
this.size = 0;
this.tombstones = 0;
}
/**
- Used for InheritableThreadLocals.
*/
Values(Values fromParent) {
this.table = fromParent.table.clone();
this.mask = fromParent.mask;
this.size = fromParent.size;
this.tombstones = fromParent.tombstones;
this.maximumLoad = fromParent.maximumLoad;
this.clean = fromParent.clean;
inheritValues(fromParent);
}
/**
-
Inherits values from a parent thread.
*/
@SuppressWarnings({"unchecked"})
private void inheritValues(Values fromParent) {
// Transfer values from parent to child thread.
Object[] table = this.table;
for (int i = table.length - 2; i >= 0; i -= 2) {
Object k = table[i];if (k == null || k == TOMBSTONE) { // Skip this entry. continue; } // The table can only contain null, tombstones and references. Reference<InheritableThreadLocal<?>> reference = (Reference<InheritableThreadLocal<?>>) k; // Raw type enables us to pass in an Object below. InheritableThreadLocal key = reference.get(); if (key != null) { // Replace value with filtered value. // We should just let exceptions bubble out and tank // the thread creation table[i + 1] = key.childValue(fromParent.table[i + 1]); } else { // The key was reclaimed. table[i] = TOMBSTONE; table[i + 1] = null; fromParent.table[i] = TOMBSTONE; fromParent.table[i + 1] = null; tombstones++; fromParent.tombstones++; size--; fromParent.size--; }
}
}
/**
- Creates a new, empty table with the given capacity.
*/
private void initializeTable(int capacity) {
this.table = new Object[capacity * 2];
this.mask = table.length - 1;
this.clean = 0;
this.maximumLoad = capacity * 2 / 3; // 2/3
}
/**
Cleans up after garbage-collected thread locals.
-
jxy 每次在新增或刪除entry后需要重新檢查table狀態(tài)瞧预,進(jìn)行更新
*/
private void cleanUp() {
if (rehash()) {
// If we rehashed, we needn't clean up (clean up happens as
// a side effect).
return;
}if (size == 0) {
// No live entries == nothing to clean.
return;
}// Clean log(table.length) entries picking up where we left off
// last time.
int index = clean;
Object[] table = this.table;
// jxy 此處沒有全部遍歷所有的item屎债,猜測是防止性能問題
for (int counter = table.length; counter > 0; counter >>= 1, index = next(index)) {
Object k = table[index];if (k == TOMBSTONE || k == null) { continue; // on to next entry } // The table can only contain null, tombstones and references. @SuppressWarnings("unchecked") Reference<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) k; if (reference.get() == null) { // This thread local was reclaimed by the garbage collector. table[index] = TOMBSTONE; table[index + 1] = null; tombstones++; size--; }
}
// Point cursor to next index.
clean = index;
}
/**
Rehashes the table, expanding or contracting it as necessary.
Gets rid of tombstones. Returns true if a rehash occurred.
We must rehash every time we fill a null slot; we depend on the
presence of null slots to end searches (otherwise, we'll infinitely
-
loop).
*/
private boolean rehash() {
if (tombstones + size < maximumLoad) {
return false;
}int capacity = table.length >> 1;
// Default to the same capacity. This will create a table of the
// same size and move over the live entries, analogous to a
// garbage collection. This should only happen if you churn a
// bunch of thread local garbage (removing and reinserting
// the same thread locals over and over will overwrite tombstones
// and not fill up the table).
int newCapacity = capacity;if (size > (capacity >> 1)) {
// More than 1/2 filled w/ live entries.
// Double size.
newCapacity = capacity * 2;
}Object[] oldTable = this.table;
// Allocate new table.
initializeTable(newCapacity);// We won't have any tombstones after this.
this.tombstones = 0;// If we have no live entries, we can quit here.
if (size == 0) {
return true;
}// Move over entries.
for (int i = oldTable.length - 2; i >= 0; i -= 2) {
Object k = oldTable[i];
if (k == null || k == TOMBSTONE) {
// Skip this entry.
continue;
}// The table can only contain null, tombstones and references. @SuppressWarnings("unchecked") Reference<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) k; ThreadLocal<?> key = reference.get(); if (key != null) { // Entry is still live. Move it over. add(key, oldTable[i + 1]); } else { // The key was reclaimed. size--; }
}
return true;
}
/**
- Adds an entry during rehashing. Compared to put(), this method
- doesn't have to clean up, check for existing entries, account for
- tombstones, etc.
*/
void add(ThreadLocal<?> key, Object value) {
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == null) {
table[index] = key.reference;
table[index + 1] = value;
return;
}
}
}
/**
Sets entry for given ThreadLocal to given value, creating an
-
entry if necessary.
*/
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;// jxy 為什么需要循環(huán),和TOMBSTONE定義說明結(jié)合
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; }
}
}
/**
Gets value for given ThreadLocal after not finding it in the first
-
slot.
*/
Object getAfterMiss(ThreadLocal<?> key) {
Object[] table = this.table;
int index = key.hash & mask;// If the first slot is empty, the search is over.
if (table[index] == null) {
Object value = key.initialValue();// If the table is still the same and the slot is still empty... // jxy 此處為什么要增加判斷垢油?如果是多線程的話盆驹,此類的實(shí)例都應(yīng)該是在一個(gè)線程中運(yùn)行啊
- Creates a new thread-local variable.
// 增加判斷是因?yàn)樯厦鎘ey.initalValue()的調(diào)用,在ThreadLocal中無法預(yù)知使用者如何實(shí)現(xiàn)initalValue滩愁,如果在其中做了諸如put之類的操作有可能導(dǎo)致rehash操作躯喇,此時(shí)table就會不相等,出現(xiàn)錯(cuò)誤
if (this.table == table && table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
// The table changed during initialValue().
put(key, value);
return value;
}
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
// Continue search.
for (index = next(index);; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
return table[index + 1];
}
// If no entry was found...
if (reference == null) {
Object value = key.initialValue();
// If the table is still the same...
if (this.table == table) {
// If we passed a tombstone and that slot still
// contains a tombstone...
if (firstTombstone > -1
&& table[firstTombstone] == TOMBSTONE) {
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
// No need to clean up here. We aren't filling
// in a null slot.
return value;
}
// If this slot is still empty...
if (table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
}
// The table changed during initialValue().
put(key, value);
return value;
}
if (firstTombstone == -1 && reference == TOMBSTONE) {
// Keep track of this tombstone so we can overwrite it.
firstTombstone = index;
}
}
}
/**
* Removes entry for the given ThreadLocal.
*/
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;
}
}
}
/**
* Gets the next index. If we're at the end of the table, we wrap back
* around to 0.
*/
private int next(int index) {
return (index + 2) & mask;
}
}
}