java面试--ThreadLocal线程变量

ThreadLocal是Java中用于线程内部数据存储的类,提供了线程隔离的变量副本。每个线程都有自己的ThreadLocalMap,存储了特定线程的变量副本。ThreadLocalMap通过Entry实现,Entry是弱引用,防止内存泄漏。使用ThreadLocal要注意及时调用remove避免内存溢出。常见用途包括线程间数据隔离、事务信息存储和Session管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:

threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据,对其他线程而言是隔离的(在每个线程的set()方法之后,只能get到本线程的变量的副本,对其余的线程是不可见的)Thread为每一个变量在线程中都创建了副本,每一个线程都可以访问自己的内部变量副本,就ThreadLocal本身而言,他是不存储值的,存储在ThreadLocalMap中

实际上,ThreadLocal的静态内部类ThreadLocalMap为每个Thread都维护了一个数组table,ThreadLocal确定了一个数组下标,而这个下标就是value存储的对应位置。同一种类型的数据只有一个ThreadLocal,而每一个线程都有一个ThreadLocalMap没一个线程都会持有一个ThreadLocalMap对象的变量threadLocalsEntry是一个弱引用,当为null的时候或者内存不足的时候,就会被回收掉。Entry的key是ThreadLocal(计算索引)

static class ThreadLocalMap {

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

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

        private static final int INITIAL_CAPACITY = 16;

        private Entry[] table;

        private int size = 0;

在实例化ThreadLocalMap的时候,会创建一个长度为16的Entry数组。通过hashCode与length位运算确定出一个索引值i[i = key.threadLocalHashCode & (len-1)进行位运算],这个i就是被存储在table数组中的位置。 

    //ThreadLocal中threadLocalHashCode相关代码.
    
    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        //自增
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

所以说,因为static的原因,在每次new ThreadLocal时因为threadLocalHashCode的初始化,会使threadLocalHashCode值自增一次,增量为0x61c88647。而0x61c88647为斐波那契散列乘数,通过它hash出来的结果分布会更加的均匀

方法:

set():

//set 方法
public void set(T value) {
      //获取当前线程
      Thread t = Thread.currentThread();
      //实际存储的数据结构类型
      ThreadLocalMap map = getMap(t);
      //如果存在map就直接set,没有则创建map并set
      if (map != null)
          map.set(this, value);
      else
          createMap(t, value);
  }
  
//getMap方法
ThreadLocalMap getMap(Thread t) {
      //thred中维护了一个ThreadLocalMap
      return t.threadLocals;
 }
 
//createMap
void createMap(Thread t, T firstValue) {
      //实例化一个新的ThreadLocalMap,并赋值给线程的成员变量threadLocals
      t.threadLocals = new ThreadLocalMap(this, firstValue);
}

所以说,每个线程持有一个ThreadLocalMap对象。每一个新的线程Thread都会实例化一个ThreadLocalMap并赋值给成员变量threadLocals,使用时若已经存在threadLocals则直接使用已经存在的对象。

get():

//ThreadLocal中get方法
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();
}
    
//ThreadLocalMap中getEntry方法
private Entry getEntry(ThreadLocal<?> key) {
       int i = key.threadLocalHashCode & (table.length - 1);
       Entry e = table[i];
       if (e != null && e.get() == key)
            return e;
       else
            return getEntryAfterMiss(key, i, e);
   }

总结:

  1. 对于某一ThreadLocal来讲,他的索引值i是确定的,在不同线程之间访问时访问的是不同的table数组的同一位置即都为table[i],只不过这个不同线程之间的table是独立的。
  2. 对于同一线程的不同ThreadLocal来讲,这些ThreadLocal实例共享一个table数组,然后每个ThreadLocal实例在table中的索引i是不同的。

ThreadLocal的用途:

  1. 在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
  2. 线程间数据隔离
  3. 进行事务操作,用于存储线程事务信息。
  4. 数据库连接,Session会话管理。

 注:

1、如果突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

2、在使用额过程中,一定要先set()在进行get(),否则会爆出空指针异常。

于2020/09/30完成

参考链接:

https://www.jianshu.com/p/3c5d7f09dfbd

https://baijiahao.baidu.com/s?id=1653790035315010634

https://blog.youkuaiyun.com/zy1994hyq/article/details/83310049

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值