ThreadLocal和线程同步机制相比:都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。ThreadLocal提供了线程安全的共享相同名称的不同对象。
在一个村中有100户人家,当大家要使用村里唯一的一台拖拉机时,需要使用同步机制。当每家骑自家的自行车时,使用ThreadLocal,虽然大家骑的都是自行车,但是是不同的自行车实例。
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增加了泛型的支持,可以看出为了能够更好更简单的被使用,不放过需要优化的任何地方
Values中为什么不把:private Object[] table;设置为泛型?
-
一个ThreadLocal只能保持一个Object,如果想保持多个应如何处理?
*/
public class ThreadLocal<T> {/* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
// jxy 已经多次看到此类注释,代码如人生/**
- 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类结合使用的,并且是一个类似map的存储类
- 在Thread类中有: java.lang.ThreadLocal.Values localValues;
- localValues在Thread类中只是定义为包内可见,但没有任何操作,就是为了给ThreadLocal类使用
- Thread类的实例在运行时,每个线程都是不同的实例,这样localValues也就不会相互干扰
- 这样两个类配合实现不同线程获取不同实例的需求。
- 如果不和Thread类配合能否实现现有功能?
- values为什么做成可以存储多个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 该函数是protected类型的,很显然是建议在子类重载该函数的,所以通常该函数都会以匿名内部类的形式被重载,以指定初始值
*/
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可以使得当ThreadLocal可以被回收,而不会因为在values中保存有引用而无法回收
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 通过定义一个static的AtomicInteger类型变量hashCounter,
- 实现每一个ThreadLocal类实例的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的幂次方?
- 在进行hash计算的时候,是使用:int index = hash & values.mask;
mask = table.length - 1;
table = new Object[capacity * 2];
- 只有当INITIAL_SIZE为2的幂次方时,mask才可以是类似:00001111的值,在扩充table时也是通过*2来计算capacity的
- hash每次的值不一样,这样计算出的index(即取hash最后几位的值)就是唯一的。
*/
private static final int INITIAL_SIZE = 16;
/**
- Placeholder for deleted entries.
- jxy 为什么要定义tombstone?
- 因为一个线程中可以定义多个ThreadLocal,但是values中的table并不是在定义ThreadLocal时校验是否需要扩充,
- 当定义量大于capacity时,此时index = hash & values.mask有可能得到重复的值,所以要跟踪哪些entry已经废弃
- 在put时需要进行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中每一个entry是有三个状态的,null没有进行过设置,tombstone,设置过但是现在废弃了,正常设置状态
*/
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状态,进行更新
*/
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 为什么需要循环,和TOMBSTONE定义说明结合
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 此处为什么要增加判断?如果是多线程的话,此类的实例都应该是在一个线程中运行啊
- Creates a new thread-local variable.
// 增加判断是因为上面key.initalValue()的调用,在ThreadLocal中无法预知使用者如何实现initalValue,如果在其中做了诸如put之类的操作有可能导致rehash操作,此时table就会不相等,出现错误
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;
}
}
}