共计 3693 个字符,预计需要花费 10 分钟才能阅读完成。
ThreadLocal
作用
ThreadLocal
是本地线程变量,而 ThreadLocal
中寄存的是该线程的变量,且只能被本线程拜访。ThreadLocal
为每个线程都创立了变量正本,从而防止了并发拜访抵触。
上面让咱们来看一个例子:
public class ThreadLocalTest02 {public static void main(String[] args) {ThreadLocal<String> local = new ThreadLocal<>();
IntStream.range(0, 10).forEach(i -> new Thread(() -> {local.set(Thread.currentThread().getName() + ":" + i);
System.out.println("线程:" + Thread.currentThread().getName() + ",local:" + local.get());
}).start());
}
}
输入后果:线程:Thread-0,local:Thread-0:0
线程:Thread-1,local:Thread-1:1
线程:Thread-2,local:Thread-2:2
线程:Thread-3,local:Thread-3:3
线程:Thread-4,local:Thread-4:4
线程:Thread-5,local:Thread-5:5
线程:Thread-6,local:Thread-6:6
线程:Thread-7,local:Thread-7:7
线程:Thread-8,local:Thread-8:8
线程:Thread-9,local:Thread-9:9
从后果能够看到,每一个线程都有本人的 local 值,这就是 TheadLocal 的根本应用。
上面咱们从源码的角度来剖析一下,ThreadLocal 的工作原理。
ThreadLocal 源码剖析
1、set
办法
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
// 首先获取以后线程对象
Thread t = Thread.currentThread();
// 获取线程中变量 ThreadLocal.ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果不为空,if (map != null)
map.set(this, value);
else
// 如果为空,初始化该线程对象的 map 变量,其中 key 为以后的 threadlocal 变量
createMap(t, value);
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
// 初始化线程外部变量 threadLocals,key 为以后 threadlocal
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
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);
}
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {super(k);
value = v;
}
}
其中 ThreadLocalMap
是ThreadLocal
的一个动态外部类,外面定义了 Entry
来保留数据。而且是继承的弱援用。在 Entry
外部应用 ThreadLocal
作为key
,应用咱们设置的value
作为 value
。对于每个线程外部有个ThreadLocal.ThreadLocalMap
变量,存取值的时候,也是从这个容器中来获取。
2、get
办法
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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();}
通过下面的剖析,置信你对该办法曾经有所了解了,首先获取以后线程,而后通过key threadlocal
获取设置的value
。
ThreadLocal 内存泄露问题是怎么导致的?
ThreadLocalMap
中应用的 key 为 ThreadLocal
的弱援用,而 value 是强援用。所以,如果 ThreadLocal
没有被内部强援用的状况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。
这样一来,ThreadLocalMap
中就会呈现 key 为 null 的 Entry。如果咱们不做任何措施的话,value 永远无奈被 GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap
实现中曾经思考了这种状况,在调用 set()
、get()
、remove()
办法的时候,会清理掉 key 为 null 的记录。应用完 ThreadLocal
办法后最好手动调用 remove()
办法
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {super(k);
value = v;
}
}
弱援用介绍:
如果一个对象只具备弱援用,那就相似于 可有可无的生活用品。弱援用与软援用的区别在于:只具备弱援用的对象领有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具备弱援用的对象,不论以后内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,因而不肯定会很快发现那些只具备弱援用的对象。
弱援用能够和一个援用队列(ReferenceQueue)联结应用,如果弱援用所援用的对象被垃圾回收,Java 虚拟机就会把这个弱援用退出到与之关联的援用队列中。
著作权归 Guide 所有 原文链接:https://javaguide.cn/java/concurrent/java-concurrent-question…
参考文献:ThreadLocal,一篇文章就够了 – 知乎 (zhihu.com)