目录
1、thraedlocal是什么
2、如何使用threadlocal
3、实际应用代码编写
4、threadlocal源码分析
5、threadlocal内存泄漏问题
threadlocal是什么
ThreadLocal是jdk中的一个类,在多线程并发中,为每个线程提供一个内部的局部变量。实现在同一线程内不同组件间的数据传递,且多个线程不相互影响即线程安全。
定义中的核心 1、多线程并发 2、数据传递 3、线程安全
threadlocal为我们提供了三个核心的方法
方法名 | 说明 |
---|---|
public void set( T value) | 设置当前线程绑定的局部变量 |
public T get() | 获取当前线程绑定的局部变量 |
public void remove() | 移除当前线程绑定的局部变量 |
如何使用threadlocal
简单感受下threadlocal的应用
public class ThreadLocalDemo {
/**
* 全局的一个变量
*/
private static ThreadLocal<String> tl = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
tl.set("main thread");
Thread thread = new Thread(()->{
tl.set("sub thread");
System.out.println(Thread.currentThread().getName()+":::::" + tl.get());
});
thread.start();
thread.join();
System.out.println(Thread.currentThread().getName() + ":::::" + tl.get());
}
}
threadlocal使用场景分析
我们在实际开发中会分层,比如controller-service-dao层,如果说dao层需要调用controller层中的一个对象,那么该怎么做呢,常见的想法,在每个方法上的参数里面,带上那个对象,如上图所示。这样耦合极大。
那我们是否有更好的解决方法?下面就介绍下threadlocal这个对象。
//见代码
threadlocal源码分析
在进行源码分析前,我们先了解下threadlocal的结构
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();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal之set流程: 1、获取当前线程t; 2、返回当前线程t的成员变量ThreadLocalMap(以下简写map); 3、map不为null,则更新以当前线程为key的ThreadLocalMap,否则创建一个ThreadLocalMap,其中当前线程t为key;
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();
}
ThreadLocal之get流程: 1、获取当前线程t; 2、返回当前线程t的成员变量ThreadLocalMap(以下简写map); 3、map不为null,则获取以当前线程为key的ThreadLocalMap的Entry(以下简写e),如果e不为null,则直接返回该Entry的value; 4、如果map为null或者e为null,返回setInitialValue()的值。setInitialValue()调用重写的initialValue()返回新值(如果没有重写initialValue将返回默认值null),并将新值存入当前线程的ThreadLocalMap(如果当前线程没有ThreadLocalMap,会先创建一个)。
threadlocal内存泄露问题
每个thread中都存在一个map,map的类型是ThreadLocal.ThreadLocalMap,Map中的key为一个threadlocal实例。这个Map的确使用了弱引用,不过弱引用只是针对key。每个key都弱引用指向threadlocal。当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收。 但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用。只有当前thread结束以后,current thread就不会存在栈中,强引用断开,Current Thread,Map,value将全部被GC回收。 所以得出一个结论就是只要这个线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,就发生了我们认为的内存泄露。其实这是一个对概念理解的不一致,也没什么好争论的。最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束是不会销毁的,会再次使用的。就可能出现内存泄露。 在使用完成后注意调用remove方法清理。养成良好习惯