ThreadLocal 提供了线程本地变量。这些变量不同于普通变量,每个线程都可以通过 ThreadLocal 的 get 或 set 方法访问这个线程自己的变量,独立初始化变量的副本。ThreadLocal 实例通常在其他类中是 private static 域,它希望将自己的状态与一个线程关联起来。
说的通俗点就是:如果我们想要让一个变量在每一个线程中都拥有独立的值,就需要用到ThreadLocal。
验证线程变量的隔离性
我们先写一段 Android 测试代码:
public class ThreadTestActivity extends AppCompatActivity { private static final String TAG = "ThreadTestActivity "; private static ThreadLocal<String> mStringThreadLocal = new ThreadLocal<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_acc); mStringThreadLocal.set("[Thread#main]"); Log.e(TAG, "[Thread#main] ThreadLocal String =" + mStringThreadLocal.get()); new Thread("Thread#1") { @Override public void run() { mStringThreadLocal.set("[Thread#1]"); Log.d(TAG, "[Thread#1] ThreadLocal String =" + mStringThreadLocal.get()); } }.start(); new Thread("Thread#2") { @Override public void run() { Log.w(TAG, "[Thread#2] ThreadLocal String =" + mStringThreadLocal.get()); } }.start(); } }
程序运行的结果如下图所示:
从图中的运行结果来看,第一次在主线程中调用成员变量 mStringThreadLocal 的 set 方法保存了“[Thread#main]”,所以在主线程中调用 get方法 时返回的值是 “[Thread#main]” ;
然后我们在子线程Thread#1中保存了“[Thread#1]”,调用 get方法 时返回的值是 “[Thread#1]” ;
最后我们在子线程Thread#2中没有保存任何内容,直接调用 get方法 时返回的值是 null。
类ThreadLocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值 是可以放入 ThreadLocal 中进行保存的。
ThreadLocal 源码解析
那么ThreadLocal 是如何做到的呢?我们先看下ThreadLocal 的set()方法:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //如果当前线程的ThreadLocalMap为空,就new一个Map,否则以当前ThreadLocal对象为key值,存入线程的Map中 if (map != null) map.set(this, value); else createMap(t, value); }
在 set() 方法中,首先拿到了当前所在线程,就是我们调用ThreadLocal 的 set() 方法时所在的线程,然后执行了 getMap() 方法:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
getMap()方法返回了当前线程所持有的ThreadLocalMap,我们进入 Thread 类中,CTRL+F 搜索 threadLocals,可以找到 Threadd 的成员变量 threadLocals :
ThreadLocal.ThreadLocalMap threadLocals = null;
也就是说,每一个 Thread 类中都持有一个ThreadLocal.ThreadLocalMap。而我们需要保存的变量值就是保存在这个线程的 ThreadLocalMap 中的。
ThreadLocal的代码总体比较简单,没有什么可讲的,我们直接进入ThreadLocal中的嵌套内部类ThreadLocalMap的实现,这里才是关键。