前言:ThreadLocal源码学习
简介
ThreadLocal采用线程隔离的方式存放数据(存放的值是线程封闭,线程间互斥),可以避免多线程之间出现数据访问冲突。
对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
所以ThreadLocal重要作用在于线程内(单个线程内部)的数据共享,保证各个线程间数据安全,每个线程的数据不会被另外线程访问和破坏,多线程间数据隔离
PS:个人建议先学习ThreadLocal的静态内部类ThreadLocalMap,然后再来看ThreadLocal,ThreadLocal比较简单,复杂的地方都在ThreadLocalMap 常用方法
- ThreadLocal.get: 以ThreadLocal为key获取数据。
- ThreadLocal.set: 以ThreadLocal为key存放数据
- ThreadLocal.remove: 以ThreadLocal为key删除数据。
- ThreadLocal.initialValue:
对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式,使用其内部静态类ThreadLocalMap存储线程的变量
类图
字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
private static final int HASH_INCREMENT = 0x61c88647;
private static AtomicInteger nextHashCode = new AtomicInteger();
private final int threadLocalHashCode = nextHashCode();
|
SuppliedThreadLocal
SuppliedThreadLocal是ThreadLocal的一个扩展,用来支持java8函数式接口等新特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) { this.supplier = Objects.requireNonNull(supplier); }
@Override protected T initialValue() { return supplier.get(); } }
|
ThreadLocalMap
因为内容较多,详情见本站ThreadLocalMap源码学习一文
构造方法
默认构造方法,仅创建一个TreadLocal
1 2
| public ThreadLocal() { }
|
childValue
childValue方法在子类InheritableThreadLocal中定义
1 2 3
| T childValue(T parentValue) { throw new UnsupportedOperationException(); }
|
createInheritedMap
根据传入的Map创建ThreadLocalMap
1 2 3
| static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
|
createMap
set、setInitialValue方法中被调用,创建ThreadLocalMap类对象并传入firstValue来初始化
1 2 3
| void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
|
get
常用方法,返回当前线程副本中的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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
返回与当前线程关联的ThreadLocalMap,threadLocals是Thread的属性
1 2 3
| ThreadLocalMap (Thread t) { return t.threadLocals; }
|
initialValue
初始化,可以重写initialValue()来设置初始value值
1 2 3
| protected T initialValue() { return null; }
|
nextHashCode
计算下一个hashcode
1 2 3
| private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
|
remove
删除ThreadLocal中该线程
1 2 3 4 5
| public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
|
set
设置此线程的数据,注意set传的参数是this,即当前ThreadLocal对象,所以说ThreadLocalMap是以ThreadLocal对象为key
1 2 3 4 5 6 7 8 9 10 11
| public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
|
setInitialValue
初始化,由get方法调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
|
withInitial
用于支持java8函数式编程
1 2 3
| public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { return new SuppliedThreadLocal<>(supplier); }
|
简单实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class ThreadLocalTest { private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(); private static final int ThreadNum = 3;
public static void main(String[] args) { Integer integer = new Integer("0"); ThreadLocalTest.threadLocal.set(integer); if (ThreadLocalTest.threadLocal.get() != null) { System.out.println("主线程 : " + ThreadLocalTest.threadLocal.get()); for (int i = 0; i < ThreadNum; i++) { String name = "ThreadTask" + i; new Thread(new MyThreadTask(name)).start(); } }
}
public static class MyThreadTask implements Runnable { private String name;
MyThreadTask(String name) { this.name = name; }
@Override public void run() { Integer integer = new Integer("1"); ThreadLocalTest.threadLocal.set(integer); for (int i = 0; i < 5; i++) { System.out.println("线程" + name + ": " + ((Integer) ThreadLocalTest.threadLocal.get() + i)); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }
} }
|
可以看到各线程中的变量都是相互独立、互不影响
总结
Thread、ThreadLocal、ThreadLocalMap三者关系如下图
线程调用ThreadLocal对象get、set,第一次调用set时会创建一个ThreadLocalMap对象,注意这个对象是属于线程的。然后以ThreadLocal对象为key存储数据,这里也要注意