ThreadLocal学习

ThreadLocal

ThreadLocal 是一种线程局部变量,它允许每个线程在自己的作用域内存储和访问变量。ThreadLocal 变量的值只能被当前线程所访问和修改,其他线程无法访问该变量。

ThreadLocal 提供了一种方便的方式来管理多线程环境下的共享状态,例如在 Web 应用程序中,可以使用 ThreadLocal 来存储用户会话 ID,以便在多个请求之间共享。ThreadLocal 还可以用于实现线程安全的数据结构,例如计数器或队列。

使用 ThreadLocal 时需要注意以下几点:

  1. 每个 ThreadLocal 实例都是独立的,因此需要为每个线程创建一个 ThreadLocal 对象。
  2. ThreadLocal 变量的默认初始值为 null,因此需要显式地设置初始值。
  3. 在不再需要 ThreadLocal 变量时,应该及时清除其引用,以避免内存泄漏
public class ThreadLocal<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);
    }
	
	...
}

ThreadLocal 将自己作为key,将数据存入ThreadLocalMap 中

ThreadLocalMap

什么是ThreadLocalMap

    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
//------------------------------------------------------//
public class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocalMap 是 Thread 的成员变量
所以 Thread ThreadLocalMap 与 ThreadLocal 关系如下:

  1. Thread 与 ThreadLocalMap是一对一,一个线程维护一个ThreadLocalMap
  2. Thread 与 ThreadLocal是一对多,一个线程可以拥有多个ThreadLocal对象,分别hash映射
 static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

通过构建 entity 保存数据,其中作为key的ThreadLocal,是WeakReference弱引用。
为什么使用弱引用

由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。
因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
而且ThreadLocalMap在内部的set,get和扩容时都会清理掉泄漏的Entry(Java为了最小化减少内存泄露的可能性和影响,在ThreadLocal的get,set的时候都会清除线程Map里所有key为null的value。)内存泄漏完全没必要过于担心用

但是在特殊情况下,例如配合线程池使用时,由于线程一直存在,若未及时进行remove,会导致内存溢出。
1.线程池是长生命周期,使用完后不会主动释放资源
2.Thread–>ThreadLocalMap–>Entry[ ] key,value() key是弱引用,value是强引用,垃圾回收器不会回收value资源,内存无法释放

使用场景

web系统中,保存用户的user信息,在多个service之间传递

class service1 {
    public void method1() {
        System.out.println(" service1 get session "
                + SessionContextHolder.holder.get());
        service2 service2 = new service2();
        service2.method1();
    }
}

class service2 {
    public void method1() {
        System.out.println(" service2 get session "
                + SessionContextHolder.holder.get());
        service3 service3 = new service3();
        service3.method1();
    }
}

class service3 {
    public void method1() {
        System.out.println(" service2 get session "
                + SessionContextHolder.holder.get());
    }
}

class SessionContextHolder {
    public static ThreadLocal<String> holder = new ThreadLocal<>();
}

参考文章

ThreadLocal内存泄漏案例分析实战: https://juejin.cn/post/6982121384533032991
ThreadLocal https://blog.csdn.net/m0_46628950/article/details/126448853

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ThreadLocal是一种特殊的变量存储类,它允许每个线程拥有自己的副本变量,从而避免了线程之间的共享变量冲突。在Java中,ThreadLocal通常用于存储线程局部数据,即每个线程都有自己的数据副本,而不会受到其他线程的影响。 要使用ThreadLocal,首先需要创建一个ThreadLocal对象,并使用其set()方法将数据存储在当前线程的副本中。例如: ```java ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); threadLocal.set(42); ``` 在这个例子中,我们创建了一个ThreadLocal对象threadLocal,并将其初始化为空值。然后,我们使用set()方法将整数值42存储在当前线程的副本中。 接下来,我们可以通过get()方法从当前线程中获取存储在该ThreadLocal对象中的值。例如: ```java int value = threadLocal.get(); ``` 这个例子中,我们从当前线程的副本中获取了存储在threadLocal中的值,并将其存储在变量value中。由于每个线程都有自己的副本,因此我们可以通过这种方式在不同的线程之间传递数据。 需要注意的是,ThreadLocal中的数据存储在每个线程的本地内存中,因此如果一个线程修改了存储在ThreadLocal中的值,它不会影响其他线程中的副本。这意味着ThreadLocal通常用于存储需要在多个线程之间隔离的数据。 除了set()和get()方法外,ThreadLocal还提供了remove()方法来删除当前线程中的数据副本。此外,还可以使用getAndSet()方法来获取当前线程中的值并设置新的值。 总之,ThreadLocal是一种非常有用的工具,它允许每个线程拥有自己的数据副本,从而避免了共享变量之间的冲突。它通常用于需要隔离不同线程之间的数据的情况。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值