一个例子(参考自张孝祥老师的视频)
代码
package com.jue.test_thread_local; import java.util.Random; public class TestMain { private static ThreadLocal<Integer> tl = new ThreadLocal<Integer>(); public static void main(String[] args) { TestMain tm = new TestMain(); tm.test(); } public void test() { for (int i = 0; i < 3; i++) { new Thread() { @Override public void run() { int data = new Random().nextInt(200); tl.set(data); System.out.println(Thread.currentThread().getName() + " thread data = " + data); new A().testA(); } }.start(); } } static class A { public void testA() { System.out.println("A -> testA() " + Thread.currentThread().getName() + " thread data " + tl.get()); } } }
结果
TestMain-> test Thread-1 thread data = 199 TestMain-> test Thread-2 thread data = 16 TestMain-> test Thread-0 thread data = 55 A->testA() Thread-2 thread data 16 A->testA() Thread-1 thread data 199 A->testA() Thread-0 thread data 55
每个线程一个单例的实现
ThreadSingleton
public class ThreadSingleton { private String threadName; private static ThreadLocal<ThreadSingleton> sThreadLocal = new ThreadLocal<ThreadSingleton>(); private ThreadSingleton(String name) { threadName = name; } public static ThreadSingleton getInstance() { if (sThreadLocal.get() == null) { String name = Thread.currentThread().getName(); sThreadLocal.set(new ThreadSingleton(name)); } return sThreadLocal.get(); } @Override public String toString() { return "ThreadSingleton, threadName = " + threadName + " #" + this.hashCode(); } }
注意:getInstance方法不需要加synchronized同步锁
TestMain
public class TestMain { public static void main(String args[]) { for (int i = 0; i < 5; i++) { new Thread() { @Override public void run() { ThreadSingleton ts1 = ThreadSingleton.getInstance(); ThreadSingleton ts2 = ThreadSingleton.getInstance(); System.out.println(ts1); System.out.println(ts2); } }.start(); } } }
输出结果
ThreadSingleton, threadName = Thread-2 #1024915921 ThreadSingleton, threadName = Thread-2 #1024915921 ThreadSingleton, threadName = Thread-3 #2121724417 ThreadSingleton, threadName = Thread-3 #2121724417 ThreadSingleton, threadName = Thread-0 #1647036297 ThreadSingleton, threadName = Thread-0 #1647036297 ThreadSingleton, threadName = Thread-1 #113690433 ThreadSingleton, threadName = Thread-4 #185211847 ThreadSingleton, threadName = Thread-1 #113690433 ThreadSingleton, threadName = Thread-4 #185211847
源码分析
字段
private final int threadLocalHashCode = nextHashCode();
记录当前hashCode值
方法
返回下一个hashCode
/** * Returns the next hash code. */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
返回当前线程存储的 thread-local variable
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
我们查看getMap(t)方法,发现访问的是Thread类的threadLocals字段
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
查看Thread类发现下面的字段,即:thread-local variable 是因为thread对象的间接持有
ThreadLocal.ThreadLocalMap threadLocals = null;
设置当前线程的thread -local variable
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-local variable
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
静态内部类ThreadLocalMap
静态内部类Entry
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
Entry继承自WeakReferenceEntry key为ThreadLocal<?>,value为ThreadLocal的T
当key为null的时候,表示key不再引用,代表实体可以从表中除去
Entry数组
private Entry[] table;
构造器
关于ThreadLocalMap
- ThreadLocalMap是一个用户自定义的hash map。
- ThreadLocalMap适合掌管线程本地数据
- ThreadLocalMap没有暴露任何操作给ThreadLocal类之外的类
- ThreadLocalMap package内可见,为了允许Thread类的访问
- 为了处理大个,生命周期长的使用情况,hash表entry使用WeakReference做key
- 引用队列如果没有使用,entry只能保证在空间不足的时候remove掉
ThreadLocal 与 ThreadLocalMap 、Entry、Thread的关系
- 一个线程持有一个ThreadLocalMap引用
- 一个ThreadLocalMap可以持有多个Entry,ThreadLocalMap本身就持有Entry 数组的引用
- 一个Entry持有一个ThreadLocal的Weak Reference
- 间接的:一个线程可以持有多个ThreadLocal的引用
- 一个ThreadLocal引用可以被多个线程分别持有