ThreadLocal
ThreadLocal(线程的隔离)
ThreadLocal 使每一个线程有独立的副本:它 提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。
ThreadLocal的应用场景:
在多线程并发情况下,有一个共享变量,不同线程设置不同值后,各线程只获取自己设置的值
代码演示:
public class ThreadLocalTest { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 1; } }; /** * 测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响 */ private static class ThreadInfo extends Thread{ int num; public ThreadInfo(int num){ this.num = num; } @Override public void run() { int tl = threadLocal.get(); tl += num; threadLocal.set(tl); System.out.println(Thread.currentThread().getName()+"---threadLocal.get():"+threadLocal.get()); } } public static void main(String[] args) { int times = 5; Thread[] threads = new Thread[times]; for (int i = 0; i < times; i++) { threads[i] = new ThreadInfo(i); } for (int i = 0; i < times; i++) { threads[i].start(); } } }
ThreadLocal运行结果:
每一个线程获取的ThreadLocal都是独立的。
无ThreadLocal的代码演示:
public class NoThreadLocal { static int noThreadLocal = 1; private static class ThreadInfo extends Thread{ public int num; public ThreadInfo(int num){ this.num = num; } @Override public void run() { noThreadLocal = noThreadLocal + num; System.out.println( Thread.currentThread().getName()+"---:"+noThreadLocal); } } public static void main(String[] args) { int times = 5; ThreadInfo[] threads = new ThreadInfo[times]; for (int i = 0; i < times; i++) { threads[i] = new ThreadInfo(i); } for (int i = 0; i < times; i++) { threads[i].start(); } } }
无ThreadLocal运行结果:
ThreadLocal的实现原理:
ThreadLocal的set方法:第一步是获取当前线程实例,然后通过getMap方法获取当前线程实例的threadLocals属性,threadLocals是ThreadLocalMap类型的变量,而ThreadLocalMap则是一个定制化的HashMap。
源码注释:将此线程局部变量的当前线程副本设置为指定值。大多数子类无需重写此方法,只需依赖{@link#initialValue}方法来设置线程局部变量的值。
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
如果getMap方法获取的ThreadLocalMap类型变量map不等于null,则以当前ThreadLocal实例对象为key,传入的value值为value存到这个ThreadLocalMap中,需要注意的是在实际存储的时候,key使用的是ThreadLocal的弱引用。
如果getMap方法获取的ThreadLocalMap类型变量map等于null,则调用createMap方法创建一个ThreadLocalMap实例对象,并以当前ThreadLocal实例对象为key,传入的value值为value存到这个ThreadLocalMap中。
看到这里就会明白,使用ThreadLocal时,每个线程维护一个ThreadLocalMap映射表,映射表的key是ThreadLocal实例,并且使用的是ThreadLocal的弱引用 ,value是具体需要存储的Object。
ThreadLocal的get方法:第一步是获取当前线程实例,然后获取当前线程副本中指定的值,如果getMap方法获取到的ThreadLocalMap对象实例等于null,则调用setInitialValue方法初始化一个ThreadLocalMap,并以当前ThreadLocal实例对象为key,null值为value存到这个ThreadLocalMap中,同时返回null。
源码注释:返回此线程局部变量的当前线程副本中的值。如果变量没有当前线程的值,则首先将其初始化为调用{@link#initialValue}方法返回的值。
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(); }
ThreadLocalMap map = getMap(t)的底层方法:重点注意这里是弱引用
源码注释:此哈希映射中的条目使用它的主ref字段作为键(它总是线程本地对象)。注意空键(即entry.get()==null)表示不再引用密钥,因此条目可以从表中删除。这些条目被提及作为下面代码中的“陈旧条目”
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
setInitialValue方法:返回ThreadLocal的初始值
源码注释:用于建立初始值的set()的变量。取而代之以防用户重写set()方法
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; }
流程图说明:Thread类中声明的threadLocals变量是map结构,每个线程都可以关联很多个ThreadLocal变量。
未完待续...
参考网址: