作用

ThreadLocal 类用于提供线程内部的局部变量,其在多线程环境下能保证各个线程内部变量的隔离性。
使线程中的某个值和保存至的对象关联起来。

在并发的时候,可能需要每条线程都有一个同名变量,并且该变量的值均不同. 使用一个线程共享的Map<Thread,Object>,Map中的key为线程对象,value即为需要存储的值。那么,我们只需要通过map.get(Thread.currentThread())即可获取本线程中该变量的值。
有何缺点呢?——答案就是:需要同步,效率低!
image.png

如何使用ThreadLocal

  1. public class Main{
  2. private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
  3. public void start() {
  4. for (int i=0; i<10; i++) {
  5. new Thread(new Runnable(){
  6. @override
  7. public void run(){
  8. threadLocal.set(i);
  9. threadLocal.get();
  10. threadLocal.remove();
  11. }
  12. }).start();
  13. }
  14. }
  15. }
  16. 作者:大闲人柴毛毛
  17. 链接:https://juejin.cn/post/6844903575026401288
  18. 来源:掘金
  19. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  1. 先创建一个线程共享的ThreadLocal对象,用于存放int
  2. 每个线程如下操作:
    • set(obj):向当前线程中存储数据
    • get():获取当前线程中的数据
    • remove():删除当前线程中的数据

ThreadLocal的使用方法非常简单,关键在于它背后的实现原理。回到上面的问题:ThreadLocal究竟是如何避免同步锁,从而保证读写的高效?

ThreadLocal究竟是如何避免同步锁,从而保证读写的高效?

ThreadLocal - 图2

ThreadLocal并不维护ThreadLocalMap,并不是一个存储数据的容器,它只是相当于一个工具包,提供了操作该容器的方法,如get、set、remove等。而ThreadLocal内部类ThreadLocalMap才是存储数据的容器,并且该容器由Thread维护。
每一个Thread对象均含有一个ThreadLocalMap类型的成员变量threadLocals,它存储本线程中所有ThreadLocal对象及其对应的值。
ThreadLocalMap由一个个Entry对象构成,Entry的代码如下:

  1. static class Entry extends WeakReference<ThreadLocal<?>> {
  2. Object value;
  3. Entry(ThreadLocal<?> k, Object v) {
  4. super(k);
  5. value = v;
  6. }
  7. }

Entry继承自WeakReference<ThreadLocal<?>>,一个EntryThreadLocal对象和Object构成。由此可见,Entry的key是ThreadLocal对象,并且是一个弱引用。当没指向key的强引用后,该key就会被垃圾收集器回收。

那么,ThreadLocal是如何工作的呢?下面来看set和get方法。

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. createMap(t, value);
  8. }
  9. public T get() {
  10. Thread t = Thread.currentThread();
  11. ThreadLocalMap map = getMap(t);
  12. if (map != null) {
  13. ThreadLocalMap.Entry e = map.getEntry(this);
  14. if (e != null) {
  15. @SuppressWarnings("unchecked")
  16. T result = (T)e.value;
  17. return result;
  18. }
  19. }
  20. return setInitialValue();
  21. }
  22. ThreadLocalMap getMap(Thread t) {
  23. return t.threadLocals;
  24. }

当执行set方法时,ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key,将值存储进ThreadLocalMap对象中。
get方法执行过程类似。ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key,获取对应的value。

由于每一条线程均含有各自私有的ThreadLocalMap容器,这些容器相互独立互不影响,因此不会存在线程安全性问题,从而也无需使用同步机制来保证多条线程访问容器的互斥性。