ThreadLocal-分析-总结

本文深入解析了ThreadLocal类的工作原理,介绍了ThreadLocalMap的作用及其实现细节,通过具体示例展示了ThreadLocal如何实现线程之间的变量隔离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ThreadLocal<T>类在Spring,Hibernate等框架中起到了很大的作用,对于其工作原理,很多网上的文章分析的不够彻底,甚至有些误解。

 

首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作甚密的其他几个类(内部类)

1.ThreadLocalMap

2.Thread

可能有人会觉得Thread与ThreadLocal有什么关系,其实真正的奥秘就在Thread类中的一行:

ThreadLocal.ThreadLocalMap threadLocals = null;

 

 其中ThreadLocalMap的定义是在ThreadLocal类中,真正的引用却是在Thread类中

 

那么ThreadLocalMap究竟是什么呢?

 

可以看到这个类应该是一个Map,JDK的解释是

ThreadLocalMap is a customized hash map suitable only for maintaining thread local values
 

 

接下来的重点是ThreadLocalMap中用于存储数据的entry

static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

 

 从中我们可以发现这个Map的key是ThreadLocal变量,value为用户的值,并不是网上大多数的列子key是线程的名字或者标识

 

到这里,我们就可以理解ThreadLocal究竟是如何工作的了

 

1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类

2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)

3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)

4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝

 

public class ThreadLocalSample {

	public static void main(String[] args) {
		ThreadTest test1 = new ThreadTest(10);
		ThreadTest test2 = new ThreadTest(20);
		test1.start();
		test2.start();
	}

}

class ThreadTest extends Thread {

	public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();

	public Integer num;

	public ThreadTest(Integer num) {
		super("线程" + num);
		this.num = num;
	}

	@Override
	public void run() {
		System.out.println(this);   //打印出当前运行的线程
		System.out.println(thLocal);   //打印出当前调用的 ThreadLocal

		Integer n = thLocal.get();  //看ThreadLocal是否共享,如果其中一个能够打印出值表示共享
		System.out.println("n:" + n);

		if (n == null) {
			thLocal.set(num);
		}
		System.out.println(thLocal.get());
	}
}

 

 

接下来分析一下源码,就更加清楚了

 

 ThreadLocal get方法源码

//threadlocal get方法源码
 public T get() {
        Thread t = Thread.currentThread();
        //得到当前线程的ThreadLocalMap  getMap(t) = t.threadLocals 参见下面getMap方法
        ThreadLocalMap map = getMap(t); 
        if (map != null) {
         //在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry
            ThreadLocalMap.Entry e = map.getEntry(this); 
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
 
ThreadLocal set方法源码
// threadlocal set方法源码
	public void set(T value) {
		Thread t = Thread.currentThread();
		ThreadLocalMap map = getMap(t);
		if (map != null)
			map.set(this, value);
		else
			createMap(t, value); // 可见每个thread都会根据ThreadLocal创建map
	}

创建和获取MAP方法
// threadlocal createMap方法
	void createMap(Thread t, T firstValue) {
		t.threadLocals = new ThreadLocalMap(this, firstValue);
	}
// threadlocal getMap方法
	ThreadLocalMap getMap(Thread t) {
		return t.threadLocals;
	}

 
 
初始化方法代码
private T setInitialValue() {
        /**
         * 默认返回null,这个方法为protected可以继承
         */
        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类的使用虽然是用来解决多线程的问题的,但是还是有很明显的针对性

1.最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。典型的应用莫过于Spring,Hibernate等框架中对于多线程的处理了

 

Java代码
private static final ThreadLocal threadSession = new ThreadLocal();  
  
public static Session getSession() throws InfrastructureException {  
    Session s = (Session) threadSession.get();  
    try {  
        if (s == null) {  
            s = getSessionFactory().openSession();  
            threadSession.set(s);  
        }  
    } catch (HibernateException ex) {  
        throw new InfrastructureException(ex);  
    }  
    return s;  
}  

 这段代码,每个线程有自己的ThreadLocalMap,每个ThreadLocalMap中根据需要初始加载threadSession,这样的好处就是介于singleton与prototype之间,应用singleton无法解决线程,应用prototype开销又太大,有了ThreadLocal之后就好了,对于需要线程“霸占”的变量用ThreadLocal,而该类实例的方法均可以共享。

 

2.关于内存泄漏:

虽然ThreadLocalMap已经使用了weakReference,但是还是建议能够显示的使用remove方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值