ThreadLocal学习

1、threadLocal图解

  java.lang.ThreadLocal类实现了线程的本地存储。

ThreadLocal的内部实现:

  • ThreadLocal的内部实现包括一个类似HashMap的对象,这里称之ThreadLocalMap。

  • ThreadLocalMap的key会持有对ThreadLocal实例的弱引用(Weak Reference),value会引用具体存储的对象实例。

【强引用】:

1、threalLocal对象指向threalLocalMap中的key .

2、线程对象 指向 堆中的 threalLocalMap。

【弱引用】:

1、threalLocalMap 的key为 threadLocal对象,即threadLocal对象 指向key为弱引用。

部分源码如下: 

// ThreadLocal类中的get()方法: 
	// 代码逻辑:
	// 1、通过当前线程对象,获取线程对象中的成员变量threadLocals 即threadLocalMap。
	// 2、拿到threadLocalMap后,根据当前threadLocal对象作为key,获取value值。
	// 3、如果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类中的setInitialValue()方法: 设置ThreadLocalMap的初始化值。
	private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
	// ThreadLocal类中的initialValue()方法:ThreadLocalMap初始value为null
	protected T initialValue() {
        return null;
    }
	// ThreadLocal类中的createMap方法: 初始化ThreadLocalMap,即线程的threadLocals对象初始化。
	void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
	
	// ThreadLocalMap类的构造方法:存储用的是Entry数组。Entry的key为ThreadLocal对象。
	ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }

	// ThreadLocal类中的getMap方法:
	ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
	// ThreadLocal类中的remove方法:
	public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
	// ThreadLocalMap类中的remove方法:
	private void remove(ThreadLocal<?> key) {
        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);
        for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            if (e.get() == key) {
                e.clear();
                expungeStaleEntry(i);
                return;
            }
        }
    }

	// Thread类中的成员变量threadLocal即: 线程中的ThreadLocalMap
	ThreadLocal.ThreadLocalMap threadLocals = null;

 2、ThreadLocal原理

  • Thread类中一个成员变量,ThreadLocal.ThreadLocalMap threadLocals

  • ThreadLocal本身不存储数据,像是一个工具类,基于ThreadLocal去操作ThreadLocalMap

  • ThreadLocalMap本身就是基于Entrty[]实现的,因为一个线程被可以绑定多个ThreadLocalMap,这样一来,可能需要存储多个数据,所以采用Entrty[]的形式实现。

  • 每一个线程有自己独立的ThreadLocalMap, 再基于ThreadLocald对象本身作为key,对value进行存取。

  • ThreadLocalMap的key是一个弱引用。弱引用的特点:即便对象有弱引用,在GC时,也必须被回收。如果ThreadLocal对象为局部变量,即方法执行结束后,threadLocal对象强引用消失,如果key的引用是强引用,会导致threadLocal对象无法被回收如果threadLocal是静态成员变量,则不会存在这个问题

ThreadLocal内存泄露问题:

  • 如果threadLocal对象的引用丢失即:使用的threadLocal不是静态成员变量,而是局部变量),key因为弱引用被GC回收掉,如果同时线程还没有被回收,就会导致内存泄露,内存中的value无法被回收,同时也无法被获取到。

  • 解决方法:

    • 方式一:只需要在使用完毕threadLocal对象之后,及时的调用threadLocal.remove()方法,移除该threalLocal对象key的Entry即可。(说明:threadLocal.remove()是threadLocal对象的强引用,即在移除Entry前,threalLocal对象不会失效)

    • 方式二:将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。

总结:

  • 线程中threadLocalMap(即threadLocals),是在第一次调用ThreadLocal的get或set方法时,才会创建。

  • 线程调用很深,但又不想传参时,可以使用threadLocals

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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是一种非常有用的工具,它允许每个线程拥有自己的数据副本,从而避免了共享变量之间的冲突。它通常用于需要隔离不同线程之间的数据的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值