Threadlocal源码一些分析

Threadlocal思想:每个线程里面有一个map,map的键存一个Threadlocal变量,map的值存该Threadlocal所要保存的的该线程下的线程隔离对象。所以对象副本其实是存在于各线程内部,而Threadlocal只是一个工具来完成这一切。

先看类图结构:

Threadlocal

Threadlocal是裸的,除了Object没有任何父类,没有实现任何接口。

1.成员变量

// 原子类int自增类
private static AtomicInteger nextHashCode = new AtomicInteger();

// hash增量,一个常量而已
private static final int HASH_INCREMENT = 0x61c88647;

//
private final int threadLocalHashCode = nextHashCode();

2.方法暂不罗列

private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

Threadlocal内部类ThreadLocalMap

1.成员变量

// 初始容量,必须是2的指数倍(和HashMap要求一样)
private static final int INITIAL_CAPACITY = 16;

// Entry数组
private Entry[] table;

// Threadlocal里面的Entry数量
private int size = 0;

2.方法

/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value)

ThreadLocalMap内部类Entry

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

Thread

1.成员变量

// Thread类有一个ThreadLocal.ThreadLocalMap类型的成员变量
ThreadLocal.ThreadLocalMap threadLocals = null;

正片开始

一、线程第一次set元素
  1. Test.java (测试类)
// 新建类变量threadLocal
public static ThreadLocal threadLocal= new ThreadLocal();  < — — — — — a.进去
  1. ThreadLocal
// 唯一的构造方法
public ThreadLocal() {
    }
  1. Test.java (测试类)
// 给threadLocal添加一个元素进去
threadLocal.set(10);    < — — — — — b.进去
  1. ThreadLocal
public void set(T value) {
	// 得到当前线程
    Thread t = Thread.currentThread();
    // 调用getMap()方法,传入当前线程对象
    ThreadLocalMap map = getMap(t);    < — — — — — c.进去
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
  1. ThreadLocal
// 参数:t就是当前线程
ThreadLocalMap getMap(Thread t) {
	// 返回当前线程的threadLocals成员对象
    return t.threadLocals;     < — — — — — d.返回
    }
  1. ThreadLocal
public void set(T value) {

    Thread t = Thread.currentThread();
    // 得到了当前线程的threadLocals,这就是装东西的map
    // 把前线程的threadLocals成员对象,赋给map
    ThreadLocalMap map = getMap(t); 
    // map目前是null,进入else   
    if (map != null)
        map.set(this, value);
    else
    	// 调用create方法
        createMap(t, value);    < — — — — — e.进入
}
  1. ThreadLocal
// 参数:t就是当前线程,firstValue是我们传入的对象
void createMap(Thread t, T firstValue) {
	// 新建一个ThreadLocalMap对象
	// 把ThreadLocalMap对象赋给当前线程的threadLocals成员变量
    t.threadLocals = new ThreadLocalMap(this, firstValue);    < — — — — — f.进入
    }
  1. ThreadLocal.ThreadLocalMap
// ThreadLocalMap构造方法
// 传入参数:firstKey就是当前ThreadLocal对象,firstValue就是我们传入的对象
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
	// 新建Entry数组,长度为16
    table = new Entry[INITIAL_CAPACITY];
    // 计算一个threadLocalHashCode
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);     < — — — — — g.进入
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}
  1. ThreadLocal
// 以下几步主要是最终通过AtomicInteger为该ThreadLocal对象生成一个hash值
// ⚠️注意final修饰,
// 调用nextHashCode()来计算这个值
private final int threadLocalHashCode = nextHashCode();  < — — — — — h.进入

  1. ThreadLocal
/**
 * Returns the next hash code.
 */
 private static int nextHashCode() {
 	// 调用nextHashCode的getAndAdd()方法
 	// nextHashCode是一个AtomicInteger类的类成员变量
    return nextHashCode.getAndAdd(HASH_INCREMENT);  < — — — — — i.进入
    }
  1. AtomicInteger
// 该方法是AtomicInteger类的原子性自增方法
// AtomicInteger通过CAS实现原子性操作,是通过硬件实现的
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;    < — — — — — j.一路返回
    }
  1. ThreadLocal.ThreadLocalMap
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    // threadLocalHashCode 按位与 1111,其实就是取模运算,得到桶的index
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);     
    // 在i桶处新建Entry
    // firstKey就是当前ThreadLocal对象
    // firstValue我们传入的对象
    table[i] = new Entry(firstKey, firstValue);
    // ThreadLocal的size=1
    size = 1;
    // 设置阈值
    setThreshold(INITIAL_CAPACITY);
}
二、线程第二次set元素
  1. ThreadLocal
// 再次设置一个元素进去
threadLocal.set(20);    < — — — — — a.进入
  1. ThreadLocal
// 参数value就是我们传入的值
public void set(T value) {
	// 得到当前线程
    Thread t = Thread.currentThread();
    // 根据当前线程得到ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    // map不为空,进入if
    if (map != null)
        map.set(this, value);    < — — — — — b.进入
    else
        createMap(t, value);
}
  1. ThreadLocal.ThreadLocalMap
// 参数key就是当前ThreadLocal对象,value就是我们传入值
private void set(ThreadLocal<?> key, Object value) {

	// 得到该线程的ThreadLocalMap里面的数组
    Entry[] tab = table;
    // len = 16
    int len = tab.length;
    // 得到当前ThreadLocal的hash值,取模运算,得到桶index
    int i = key.threadLocalHashCode & (len-1);

	// 遍历所有桶
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
		
		// 一旦找到Entry的key和传入的key一样的Entry
        if (k == key) {
        	// 替换其旧值,返回
            e.value = value;
            return;
        }

        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }

	// 如果遍历了链表都未找到,证明该线程的ThreadLocalMap还没有添加过该ThreadLocal
	// 新建一个Entry,key就是该ThreadLocal,value是我们要set的值 
	// 放入第i桶
    tab[i] = new Entry(key, value);
    // 体积+1
    int sz = ++size;
    // 看是否需要扩容
    if (!cleanSomeSlots(i, sz) && sz >= threshold)   < — — — — — c.进入
        rehash();
}
  1. ThreadLocal

private boolean cleanSomeSlots(int i, int n) {
    boolean removed = false;
    Entry[] tab = table;
    int len = tab.length;
    // 循环遍历所有桶
    do {
        i = nextIndex(i, len);
        Entry e = tab[i];
        // 如果该桶不是null,但是该桶的内容是null
        if (e != null && e.get() == null) {
            n = len;
            // 设置移除标志为true
            removed = true;
            i = expungeStaleEntry(i);  //太复杂不分析了
        }
        // 只要n/2不等于0,就循环
        // n = n/2
    } while ( (n >>>= 1) != 0);
    return removed;
}

可以看出,ThreadLocalMap里面的map和HashMap是不一样的,ThreadLocalMap里面只有数组,没有链表。

如果使用线程池,那么使用了Threadlocal后需要清理掉remove,不然map还存在于thread对象中,下次就有被拿来用了。
Threadlocal里面还有个强/弱引用的问题和内存清理的问题。

⚠️⚠️spring:一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程
同一线程贯通三层这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。

发布了17 篇原创文章 · 获赞 18 · 访问量 5556

猜你喜欢

转载自blog.csdn.net/Vincentqqqqqqq/article/details/105077979