ThreadLocal可以看做线程的本地变量,实现原理就是每个线程保存一份数据的副本,数据隔离,以空间换时间。
public class Client3 { public static void main(String[] args) { ThreadLocal<String> nameThreadLocal = new ThreadLocal<>() { @Override protected String initialValue() { return "test"; } }; for (int i = 1; i <= 5; i++) { int finalI = i; new Thread(() -> { String name = "lisi" + finalI; nameThreadLocal.set(name); System.out.println(Thread.currentThread() + " set name " + name); try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + " get name " + nameThreadLocal.get()); }, "thread" + finalI).start(); } } }
输出为
Thread[thread3,5,main] set name lisi3 Thread[thread5,5,main] set name lisi5 Thread[thread2,5,main] set name lisi2 Thread[thread1,5,main] set name lisi1 Thread[thread4,5,main] set name lisi4 Thread[thread4,5,main] get name lisi4 Thread[thread5,5,main] get name lisi5 Thread[thread1,5,main] get name lisi1 Thread[thread2,5,main] get name lisi2 Thread[thread3,5,main] get name lisi3
每个线程向TheadLocal中设置值和取值,数据都是互不影响的。如果没设置值,取initialValue()方法的初始值。
/** * 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); } }
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
获取当前线程的threadLocals,类型为ThreadLocalMap,可以简单看做一个HashMap,key为ThreadLocal对象(实际上是它的弱引用),value为要设置的值。
/** * 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; } }
弱引用如果没有关联的强引用,活不过下一次GC,使用弱引用是为了方便垃圾清理。
/** * 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(); }
/** * Increment i modulo len. */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }
ThreadLocalMap使用线性探测法解决哈希冲突。
ThreadLocal源码解读