ThreadLocal,线程本地存储,为变量在每个线程中都创建了一个副本,那么每个线程可以独立地改变和访问自己的副本变量,而不会影响其它线程所对应的副本变量。从线程的角度看,目标变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。
ThreadLocal不是用来解决对象共享访问问题的,而是提供了保持对象的方法和避免参数传递的对象访问方式。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象。
在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个 threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对 Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为 value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
在Thread类中会发现有threadLocals与inheritableThreadLocals两个成员变量,都是ThreadLocal.ThreadLocalMap类。
1 2 ThreadLocal.ThreadLocalMap threadLocals = null ; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null ;
ThreadLocalMap这个类是ThreadLocal类的一个内部类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ThreadLocal <T> { ... static class ThreadLocalMap { static class Entry extends WeakReference <ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super (k); value = v; } } ... }
可以看出ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。
总的来说,实际上是通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中,threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,这样每个线程中可有多个threadLocal变量。
简单示例:
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 public class ThreadLocalTest { ThreadLocal<StringBuilder> a = new ThreadLocal <>(); ThreadLocal<String> b = new ThreadLocal <String>() { protected String initialValue () { return Thread.currentThread().getName() + "---zero" ; } }; public static void main (String[] args) throws InterruptedException { final ThreadLocalTest test = new ThreadLocalTest (); System.out.println(test.a.get()); System.out.println(test.b.get()); Thread thread1 = new Thread () { public void run () { test.a.set(new StringBuilder ("hello" )); System.out.println(test.a.get()); System.out.println(test.b.get()); }; }; thread1.start(); thread1.join(); System.out.println(test.a.get()); System.out.println(test.b.get()); } }
运行结果:
1 2 3 4 5 6 null main---zero hello Thread-0 ---zero null main---zero
InheritableThreadLocal类继承于ThreadLocal类,所以它具有ThreadLocal类的特性,但又是一种特殊的 ThreadLocal,其特殊性在于InheritableThreadLocal变量值会自动传递给所有子线程,即在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。而普通ThreadLocal变量不行。
如果一个子线程调用InheritableThreadLocal的get(),那么它将与它的父线程看到同一个对象。为保护线程安全性,应该只对不可变对象(一旦创建,其状态就永远不会被改变的对象)使用InheritableThreadLocal,因为对象被多个线程共享。
简单示例:
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 public class InheritableThreadLocalTest { InheritableThreadLocal<StringBuilder> a = new InheritableThreadLocal <>(); InheritableThreadLocal<String> b = new InheritableThreadLocal <String>(); public static void main (String[] args) throws InterruptedException { final InheritableThreadLocalTest test = new InheritableThreadLocalTest (); test.a.set(new StringBuilder ("main---hello" )); test.b.set("main---zero" ); System.out.println(test.a.get()); System.out.println(test.b.get()); Thread thread1 = new Thread () { public void run () { System.out.println(test.a.get()); System.out.println(test.b.get()); StringBuilder a = test.a.get().append("---subThread" ); test.a.set(a); test.b.set("subThread--zero" ); System.out.println(test.a.get()); System.out.println(test.b.get()); }; }; thread1.start(); thread1.join(); System.out.println(test.a.get()); System.out.println(test.b.get()); } }
运行结果:
1 2 3 4 5 6 7 8 main---hello main---zero main---hello main---zero main---hello---subThread subThread--zero main---hello---subThread main---zero
可以看出,如果InheritableThreadLocal存储的是可变性(mutable)的对象,如StringBuilder,对于主线程设置的值,子线程可以通过get函数获取,但子线程调用set函数设置新值后,对主线程没有影响,对其它子线程也没有影响,只对自己可见,但如果子线程先get获取再修改对象的属性,那么这个修改对主线程和其它子线程是可见的,因为共享的是同一个引用。为了保护线程的安全性,一般建议只传递不可变(Immuable)对象,即没有状态的对象。