Java同一线程进行多Java实例对象调用中,可以使用TheadLoca进行的数据传递。传递的数据在每个线程内部独享。
1、使用ThreadLocal。
public static void main(String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal<String>(); ThreadLocal<Integer> threadInt = new ThreadLocal<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. */ private final int threadLocalHashCode = nextHashCode();/** * Creates a thread local variable. */ public ThreadLocal() { }
其中变量threadLocalHashCode为不可变的final类型,表明该ThreadLocal实例中的该属性一直不能改变,该变量赋值语句为:
/** * The next hash code to be given out. Updated atomically. Starts at * zero. */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** * 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. */ private static final int HASH_INCREMENT = 0x61c88647; /** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.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. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
使用当前Thread当做Key,获取ThreadLocalMap。
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.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. No operations 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. */ static class ThreadLocalMap { /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table; /** * The number of entries in the table. */ private int size = 0; /** * The next size value at which to resize. */ private int threshold; // 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. * * @param t the current thread * @param firstValue value for the initial entry of the map * @param map the map to store. */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(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 = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(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. */ static class Entry extends WeakReference<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. * * @param key the thread local object * @param value the value to be set */ private void set(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; int len = tab.length; int i = 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] = new Entry(key, value); int sz = ++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. * * @param key the thread local object * @return the entry associated with key, or null if no such */ private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
若数组中的第i个不符合,则调用getEntryAfterMiss,向后遍历。
参考:
http://www.cnblogs.com/digdeep/p/4510875.html
http://wangxinchun.iteye.com/blog/1884228