多线程(四)-- ThreadLocal(一)

122 阅读2分钟

「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战

一、ThreadLocal的介绍

ThreadLocal并不是一个Thread,而是Thread的局部变量。ThreadLocal为每个线程都创建了一个副本,不会出现一个线程读取变量时而被另一个线程修改的现象,它为解决多线程程序的并发问题提供了一种新的思路 ThreadLocal的介绍.png

一、ThreadLocal、volatile 和 synchronized的区别

  • volatile只能确保操作的是同一块内存,并不能保证操作的原子性。所以volatile一般用于声明简单类型变量,使得这些变量具有原子性,即一些简单的赋值与返回操作将被确保不中断。但是当该变量的值由自身的上一个决定时,volatile的作用就将失效,这是由volatile关键字的性质所决定的。
  • ThreadLocal和Synchonized都用于解决多线程并发访问
  • synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问,它用于在多个线程间通信时能够获得数据共享
  • ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享
  • Synchronized是为了让多线程进行数据共享,而ThreadLocal为了让多线程进行数据隔离

Synchronized.png ThreadLocal.png

一、ThreadLocal源码分析

  • get(): 首先取得当前线程,再通过getMap(t)方法获取到一个ThreadLocalMap类型的map,接着调用getEntry(this)获取到<key,value>键值对(这里获取键值对传进去的是  this,而不是当前线程t)。如果获取成功,则返回value值;如果map为空,则调用setInitialValue方法返回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();
}
  • set(T value) ThreadLocalMap 为 ThreadLocal 的一个静态内部类,里面定义了Entry 来保存数据。而且是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。对于每个线程内部有个ThreadLocal.ThreadLocalMap 变量,存取值的时候,也是从这个容器中来获取
public void set(T value) {
  Thread t = Thread.currentThread();
  ThreadLocalMap map = getMap(t);
  if (map != null)
    map.set(this, value);
  else
    createMap(t, value);
}

初始化该线程对象的map变量,其中key 为当前的threadlocal 变量

void createMap(Thread t, T firstValue) {
  t.threadLocals = new ThreadLocalMap(this, firstValue);
}
static class ThreadLocalMap {
  static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
      super(k);
      value = v;
    }
  }

  ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
  }
}