为何要使用ThreadLocal?

本文介绍了ThreadLocal的作用,包括线程间隔离和在线程生命周期内获取对象。阐述了其优点,如线程安全、提高效率等。给出两种使用方式,分析了相关源码涉及的类和方法。同时提醒使用时要注意内存泄漏问题,可调用remove方法避免。

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

一、ThreadLocal作用

1. 线程间隔离,每个线程都拥有自己独立的对象

2. 在线程生命周期内可以获取到该对象

 

二、ThreadLocal优点

1. 线程安全

2. 不需要加锁,提高执行效率

3. 高效利用内存,节省开销

4. 避免繁琐的传参

 

三、两种使用方式:

1. 创建ThreadLocal时,可以控制的对象

    ThreadLocal<SimpleDateFormat> threadLocal2 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"))

2.  创建ThreadLocal时,没法控制对象

    ThreadLocal<User> threadLocal2 = new ThreadLocal<>();

    threadLocal2.set(user);

 

四、源码

    涉及到的相关类:Thread,ThreadLocal,ThreadLocalMap

 

ThreadLocal.java

1. T initialValue()

    默认实现返回空

    a. 该方法会返回当前线程对应的初始值,这是一个延迟加载的方法,只有在调用get()的时候,才会触发

    b. 当线程第一次调用get()方法访问变量时,将调用此方法,除非线程先调用了set方法,在这种情况下,不会为线程调用本initialValue方法。即:如果先调用了set()方法,则initialValue方法将不会再被调用

2. void set(T 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);
        }
    }

3. T get()

    得到线程对应的value,如果是首次调用get(),则会调用initialize来得到这个值.

    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
  • 获取当前ThreadLocal对应的entry
  • 获取entry中的value

4. void remove()

    删除线程对应的value

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }

    备注:map中的key和value都是保存在线程中的,而不是保存在ThreadLocal中

 

ThreadLocalMap.java

    每个线程Thread类里面的成员变量,最重要的一个属性 Entry[] table,可以认为是一个map,键值对:

  • 键:这个ThreadLocal
  • 值:实际需要的变量,比如user或者simpleDateFormat对象
	static class Entry extends WeakReference<ThreadLocal<?>> {
		/** The value associated with this ThreadLocal. */
		Object value;

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

六、使用ThreadLocal注意事项

    1. 内存泄漏 OOM

  • ThreadLocalMap的每个Entry都是一个对key的弱引用,同时,每个Entry都包含一个对value的强引用
  • 正常情况下,当线程终止,保存在ThreadLocal里的value会被gc回收,因为没有任何强引用
  • 若线程不终止,那么key对应的value就不能被回收,因为有引用关系: Thread->ThreadLocalMap->Entry(key为null)-> Value
  • jdk保护机制,在set,remove,rehash方法中会扫描key为null的entry,并把对应的value设置为null

    2. 调用remove避免内存泄漏

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值