内卷老员工之threadLocal

ThreadLocal

  • java ThreadLocal类相当于提供了一种线程安全的简单方法,以此创建的内容只能在线程中存储和读取,其他线程无法访问到当前线程的变量。

threadLocal与synchronized的对比

  • Synchronized是通过线程等待,通过加锁,用更长的时间换取变量共享,解决冲突。
  • ThreadLocal是通过每个线程单独一份存储空间,用更大的空间换取来解决冲突。相比于synchronized,threadLocal的变量之间不存在共享的可能性,每个变量都是单独空间内隔离的内容,与synchronized的共享实现方式有所不同。
  • 正是由于这种隔离的特殊性,threadLocal在一些场景下具有更好的使用效果。

threadLocal的一些api应用

创建threadLocal对象
private static ThreadLocal<T> tl1 = new ThreadLocal<>();
  • T为所要存储的变量类型
给threadLocal变量赋值
tl1.set("th1===test");
获取threadLocal变量值
tl1.get()
移除threadLocal变量存储的值
tl1.remove()

threadLocal的底层存取原理

  • 当调用threadLocal.set(value)方法时,通过阅读源码可以知道,将在底层调用线程的threadLocalMap对象。以当前的threadLocal对象的弱引用作为key值,以传入的值作为value值,存储到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);
}
  • 而当读取时,直接调用threadLocal.get()方法,利用当前的threadLocal对象,到threadLocalMap中读取相应的值。
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();
}

threadLocal出现oom的原理及如何避免

  • threadLocal的回收原理主要是利用弱引用。对于同一个变量同时进行强引用和弱引用。当threadLocal对象调用结束,被垃圾回收强引用结束时,弱引用的threadLocalMap对应key会在下一次gc时被垃圾回收。这种情况下会使得threadLocalMap存在key为null,value为实际存储值,且永远不会被回收导致内存泄漏或oom。

  • 实际上,当线程结束时或调用set(value)get()方法时,会自动调用remove()方法,便可自动避免内存的泄露。

  • 因此实际上出现内存泄露的情况主要是使用线程池调用threadLocal,而线程使用结束key被置为空,同时不会被再次调用set(value)get()方法。因此可以轻易推断出,想要避免threadLocal的内存泄露问题,只需要在使用结束时手动调用remove()方法,即可将value的引用释放,从根本上解决内存泄露的问题。

InheritableThreadLocal

  • 该类继承于ThreadLocal类,与其每个线程拥有自己的threadLocal变量不同,该类允许当前线程于线程的子线程共同有权限访问InheritableThreadLocal变量。
InheritableThreadLocal<String> t = new InheritableThreadLocal();
Thread t1 = new Thread(() -> {
    t.set("test");
    Thread t2 = new Thread(() -> {
        String value = t.get();
    });
});

Thread t3 = new Thread(() -> {
    t.get();
});
  • 线程t2为线程t1的子线程,因此可以直接读取t1线程所设置的t的值。而在t3线程中,由于于t1线程没有子线程的关系,因此无法读取t1线程中设置的t的值,所读取的值为null。

后记

  • 千古兴亡多少事?悠悠。不尽长江滚滚流。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值