ThreadLocal解析

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

java同一线程进行多Java实例对象调用中,可以使用TheadLoca进行的数据传递。传递的数据在每个线程内部独享。

1、使用ThreadLocal。

publicstaticvoidmain(String[] args) {

ThreadLocal<String> threadLocal =newThreadLocal<String>();

ThreadLocal<Integer> threadInt =newThreadLocal<Integer>();

System.out.PRintln(threadLocal.get()+ "--" +threadInt.get());

threadLocal.set("first");

threadInt.set(1);

System.out.println(threadLocal.get()+ "--" +threadInt.get());

threadLocal.set("second");

threadInt.set(2);

System.out.println(threadLocal.get()+ "--" +threadInt.get());

System.out.println(threadLocal.get()+ "--" +threadInt.get());

}

2、ThreadLocal类:

初始化ThreadLoca类时,构造函数为空

/*** ThreadLocals rely on per-thread linear-probe hash maps attached

* to each thread (Thread.threadLocals and

* inheritableThreadLocals). The ThreadLocal objects act as keys,

* searched via threadLocalHashCode. This is a custom hash code

* (useful only within ThreadLocalMaps) that eliminates collisions

* in the common case where consecutively constructed ThreadLocals

* are used by the same threads, while remaining well-behaved in

* less common cases.*/privatefinalintthreadLocalHashCode =nextHashCode();/*** Creates a thread local variable.*/publicThreadLocal() {

}

其中变量threadLocalHashCode为不可变的final类型,表明该ThreadLocal实例中的该属性一直不能改变,该变量赋值语句为:

/*** The next hash code to be given out. Updated atomically. Starts at

* zero.*/privatestaticAtomicInteger nextHashCode =newAtomicInteger();/*** The difference between successively generated hash codes - turns

* implicit sequential thread-local IDs into near-optimally spread

* multiplicative hash values for power-of-two-sized tables.*/privatestaticfinalintHASH_INCREMENT = 0x61c88647;/*** Returns the next hash code.*/privatestaticintnextHashCode() {returnnextHashCode.getAndAdd(HASH_INCREMENT);

}

使用了静态的类AtomicInteger,提供一个原子性的相加操作,保证每个线程之间的调用不会冲突,静态AtomicInteger保证每个ThreadLoca的初始化都不会得到相同的值。其中添加量为:0x61c88647(为什么是这个值?)。

ThreadLocal提供了可供调用的方法:

public T get()

public void set(T value)

public void remove()

先看set方法:

/*** Sets the current thread's copy of this thread-local variable

* to the specified value. Most subclasses will have no need to

* override this method, relying solely on the {@link#initialValue}

* method to set the values of thread-locals.

*

*@paramvalue the value to be stored in the current thread's copy of

* this thread-local.*/publicvoidset(T value) {

Thread t=Thread.currentThread();

ThreadLocalMap map=getMap(t);if(map !=null)

map.set(this, value);elsecreateMap(t, value);

}

使用当前Thread当做Key,获取ThreadLocalMap。

/*** Get the map associated with a ThreadLocal. Overridden in

* InheritableThreadLocal.

*

*@paramt the current thread

*@returnthe map*/ThreadLocalMap getMap(Thread t) {returnt.threadLocals;

}

最终返回的是Thread中的变量,threadLocals。

/*ThreadLocal values pertaining to this thread. This map is maintained

* by the ThreadLocal class.*/ThreadLocal.ThreadLocalMap threadLocals=null;

Thread类中的变量,threadLocals为每个线程私有的属性。同一个线程中的每一个ThreadLocal调用getMap(Thread t),都会得到相同的值, ThreadLocalMap:

/*** ThreadLocalMap is a customized hash map suitable only for

* maintaining thread local values. NoOperations are exported

* outside of the ThreadLocal class. The class is package private to

* allow declaration of fields in class Thread. To help deal with

* very large and long-lived usages, the hash table entries use

* WeakReferences for keys. However, since reference queues are not

* used, stale entries are guaranteed to be removed only when

* the table starts running out of space.*/staticclassThreadLocalMap {/*** The initial capacity -- MUST be a power of two.*/privatestaticfinalintINITIAL_CAPACITY = 16;/*** The table, resized as necessary.

* table.length MUST always be a power of two.*/privateEntry[] table;/*** The number of entries in the table.*/privateintsize = 0;/*** The next size value at which to resize.*/privateintthreshold;//Default to 0

ThreadLocalMap看名称以为Map的子类,其实为一个普通类,内部有一个数组 Entry[] table;

在ThreadLocal中的Set方法中,若Thread对象中的threadLocals为null( Thread对象中该属性默认为null),则调用createMap方法:

/*** Create the map associated with a ThreadLocal. Overridden in

* InheritableThreadLocal.

*

*@paramt the current thread

*@paramfirstValue value for the initial entry of the map

*@parammap the map to store.*/voidcreateMap(Thread t, T firstValue) {

t.threadLocals=newThreadLocalMap(this, firstValue);

}

new 出一个ThreadLocalMap对象,赋值给Thread的threadLocals属性,看构造方法:

/*** Construct a new map initially containing (firstKey, firstValue).

* ThreadLocalMaps are constructed lazily, so we only create

* one when we have at least one entry to put in it.*/ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {

table=newEntry[INITIAL_CAPACITY];inti = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

table[i]=newEntry(firstKey, firstValue);

size= 1;

setThreshold(INITIAL_CAPACITY);

}

构造时需要传入两个参数, ThreadLocal与 Object作为key-value, table数组初始化为16个, threadLocalHashCode为传入的ThreadLocal前面讲过的不变值的属性, 该值与15进行与的操作,得到ThreadLocal对应数组的index,同时设定该数组中index为new Entry对象。

/*** The entries in this hash map extend WeakReference, using

* its main ref field as the key (which is always a

* ThreadLocal object). Note that null keys (i.e. entry.get()

* == null) mean that the key is no longer referenced, so the

* entry can be expunged from table. Such entries are referred to

* as "stale entries" in the code that follows.*/staticclassEntryextendsWeakReference<ThreadLocal>{/**The value associated with this ThreadLocal.*/Object value;

Entry(ThreadLocal k, Object v) {super(k);

value=v;

}

}

Entry继承了WeakReference, 使用一个ThreadLocal 的Weak引用,关于Java中的4种引用:

a.强引用(strong reference): Object obj = new Object(); obj就为一个强引用,obj=null后, 该对象可能会被JVM回收

b.软引用(SoftReference):

SoftReference<Object> softref = new SoftReference<Object>(obj);

obj = null;

在内存不够用的时候,才会回收软引用的对象。

c.弱引用(WeakReference):

WeakReference<Object> weakRef = new WeakReference<Object>(obj);

obj = null;

该new出来的对象没有强引用连接时,下一次GC时,就会回收该对象。

d.虚引用(PhantomReference),它保存ReferenceQueue中的轨迹

引自:http://www.cnblogs.com/mengdd/p/3298852.html

此处使用弱引用,表明该ThreadLocal的Key转换成弱引用的对象。此处使用弱引用的好处不影响该:ThreadLocal k 的生命周期,若程序运行后,该ThreadLocal k连接的对象没有强引用后,该Entry中的Key很快会被回收掉,变为null,这样的话 (private Entry[] table;)中的该index的位置就可以被再次使用了。

其中 setThreshold(),设定数组的阀值为 len * 2 / 3, 初始化时为10.

ThreadLocalMap中的set方法:

/*** Set the value associated with key.

*

*@paramkey the thread local object

*@paramvalue the value to be set*/privatevoidset(ThreadLocal key, Object value) {//We don't use a fast path as with get() because it is at//least as common to use set() to create new entries as//it is to replace existing ones, in which case, a fast//path would fail more often than not.Entry[] tab=table;intlen =tab.length;inti = key.threadLocalHashCode & (len-1);for(Entry e =tab[i];

e!=null;

e= tab[i =nextIndex(i, len)]) {

ThreadLocal k=e.get();if(k ==key) {

e.value=value;return;

}if(k ==null) {

replaceStaleEntry(key, value, i);return;

}

}

tab[i]=newEntry(key, value);intsz = ++size;if(!cleanSomeSlots(i, sz) && sz >=threshold)

rehash();

}

从key.threadLocalHashCode计算得到的第i个元素

若不为null,开始向后遍历:

若Entry中的k为Key,并且value也相等,则不做操作,返回。

若Entry中的key为null,表明该Entry可以进行下一步操作(replaceStaleEntry方法),可能是替换该Entry,也可能是查其它....

若为null,

new Entry,赋值到数组中,

rehash方法中,判断是否扩容数据, *2

get方法首先从ThreadLocalMap中查找,

/*** Get the entry associated with key. This method

* itself handles only the fast path: a direct hit of existing

* key. It otherwise relays to getEntryAfterMiss. This is

* designed to maximize performance for direct hits, in part

* by making this method readily inlinable.

*

*@paramkey the thread local object

*@returnthe entry associated with key, or null if no such*/privateEntry getEntry(ThreadLocal key) {inti = key.threadLocalHashCode & (table.length - 1);

Entry e=table[i];if(e !=null&& e.get() ==key)returne;elsereturngetEntryAfterMiss(key, i, e);

}

若数组中的第i个不符合,则调用getEntryAfterMiss,向后遍历。

参考:

http://www.cnblogs.com/digdeep/p/4510875.html

http://wangxinchun.iteye.com/blog/1884228

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航