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;
}
}
}
网友评论