• ThreadLocal


    1 ThreadLocal简介

    ThreadLocal线程本地变量。为每个线程创建一个变量的副本,每个线程都只能访问自己的副本变量,实现了线程间的数据隔离。这样一来,就不会存在线程安全的问题

    每个线程都可以通过ThreadLocal.get()ThreadLocal.set()对本地变量进行操作而不会对其它线程的变量产生影响。

    以下代码是TheadLocal的使用:

    public static void main(String[] args) {
        ThreadLocal<String> tl = new ThreadLocal<>();
        Random random = new Random();
        IntStream.range(0, 5).forEach(value -> new Thread(() -> {
          tl.set(random.nextInt(10) + "");
          System.out.println(Thread.currentThread() + "的值为: " + tl.get());
          try {
            TimeUnit.SECONDS.sleep(1);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }).start());
    }
    
    // Thread[Thread-0,5,main]的值为: 4
    // Thread[Thread-3,5,main]的值为: 9
    // Thread[Thread-2,5,main]的值为: 7
    // Thread[Thread-1,5,main]的值为: 4
    // Thread[Thread-4,5,main]的值为: 8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    从运行的结构中也可以看出,每个线程都有各自的值。

    Thread类的内部维护着一个ThreadLocalMap的变量。ThreadLocalMapThreadLocal的静态内部类,在TheadLocalMap中定义了静态内部类Entry来进行数据存储,这是继承了弱引用。在ThreadLocalMap中,使用ThreadLocal作为key

    public class Thread implements Runnable {
      ThreadLocal.ThreadLocalMap threadLocals = null;
    }
    
    public class ThreadLocal<T> {
       private Entry[] table;
      
        static class ThreadLocalMap {
            static class Entry extends WeakReference<ThreadLocal<?>> {
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                  super(k);
                  value = v;
                }
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在调用ThreadLocal.set(T value)方法的时候,实际上调用的是ThreadLocalMap.set(ThreadLocal key, Object value),调用TheadLocal.get()的时候,实际上调用的是ThreadLocalMap.getEntry(ThreadLocal key)ThreadLocal本身是不存储值的,它只是作为一个key,让线程从ThreadLocalMap中获取value。以下是相关代码:

    public class ThreadLocal<T> {
      
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
              map.set(this, value);
            else
              createMap(t, value);
        }
    
        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();
        }
    
        public void remove() {
            ThreadLocalMap m = getMap(Thread.currentThread());
            if (m != null)
                m.remove(this);
        }
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    
        static class ThreadLocalMap {
    
            private Entry[] table;
    
            static class Entry extends WeakReference<ThreadLocal<?>> {
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                  super(k);
                  value = v;
              }
            }
        }
    
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    2 ThreadLocal内存泄漏

    ThreadLocalMap中的key,也就是ThreadLocal,是弱引用,当ThreadLocal == null的时候,就要被垃圾回收器回收了。但是,ThreadLocalMapThread的生命周期是一致的,它不会被回收。这个时候ThreadLocalMap中就会出现keynullvalue,就会存在这样一条引用链:Thread Ref -> Thread -> ThreadLocalMap -> Entry -> value永远无法回收,这样就造成了内存泄漏。

    其实,在ThreadLocalMap的设计中已经考虑到了这些情况,也加上了相应的防护措施,在ThreadLocalMap.get/ThreadLocalMap.set/ThreadLocalMap.remove的时候,会清除掉ThreadLocalMapkeynullvalue

    但是这些被动的预防措施并不能保证不会内存泄漏:

    • 使用staticThreadLocal,延长了ThreadLocal的生命周期,可能导致的内存泄漏;
    • 使用完ThreadLocal后,执行ThreadLocal.remove()操作;

    3 ThreadLocalsynchronized

    ThreadLocalSynchronized关键字都用于处理多线程并发访问变量的问题,不过两者处理问题的角度和思路不,前者是空间换取时间,后者是时间换取空间。

    参考

    ThreadLocal
    面试官:知道ThreadLocal嘛?谈谈你对它的理解?(基于jdk1.8)
    面试官:ThreadLocal 不是啥高级的东西

  • 相关阅读:
    redis常识
    Git的基本操作以及原理介绍
    rsync 远程数据同步
    搭建K8s集群
    【OpenCV 例程 300篇】243. 特征检测之 FAST 算法
    linux--进程通信--管道通信
    谁不是一边升学求职,一边死在路上
    危言耸听?Coinbase投资人解密加密近况,寒冬何时结束?如何应对?
    Android - AsyncTask
    腾讯云16核服务器配置大全_16核CPU型号性能测评
  • 原文地址:https://blog.csdn.net/xingyu19911016/article/details/126133813