被误解的ThreadLocal

 项目开发最常见的模式就是拿着之前开发的工程到新的项目中修修改改,这种开发方式很快,主要功能该有的都有了,只要按照新的需求改改就能用,但是开发不可能一直都是同一拨人干这事,后来的人对整个项目也不会做到100%的了解,这样新近的人就会做一些重复的工作,一两次还没啥,但是多了之后后面开发的人就会被坑死。现在就是开发这种项目过程不多说了。

最近看之前的代码,程序中对内容库session连接处理的地方感觉很奇怪,大致处理方式如下:

//定义Session保存对象
private static Map<String, ThreadLocal<Session>> map = new HashMap<String, ThreadLocal<Session>>();

//获取session方法
public Session get(String targetDocName){
	ThreadLocal<Session> threadLocalSession = map.get(map.get(targetDocName));
	Session session = null;
	if(threadLocalSession == null){
		threadLocalSession = new ThreadLocal<Session>();	
		map.put(targetDocName, threadLocalSession);
	} 
	if(threadLocalSession.get() == null){
		session = SessionUtil.getSession(targetDocName);
		threadLocalSession.set(session);
	}
	threadLocalSession.get();
	return session.get();
}

因为需要获取多个连接库的session所以最外层用Map保存,但是里面使用ThreadLocal获取session的方式就很奇怪了,我们知道ThreadLocal定义的是线程变量,在一个线程中操作一个ThreadLocal变量是不会影响到这个变量在其他线程中的正常使用的。按照这个思路,程序中设置ThreadLocal变量的值,只会对当前线程有效,所以之后的线程通过threadLocalSession来获取session都会是空值,由于ThreadLocal这个对象的特殊性,造成了他的设计思路与实际的程序运行有出入,当然这里的实现不会影响程序的正确性,只是性能上的问题。

 

下面我们就来了解下ThreadLocal这个线程变量:

首先需要了解为什么ThreadLocal定义的是一个线程变量(解释下我对线程变量的理解,就是定义的这个变量作用域是当前线程,线程结束该变量随之释放。),我们打开jdk源码查看java.lang.Thread的实现,可以看到有如下实现:

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

 Thread的定义中就包含了一个ThreadLocal的属性,也就是说每一个线程执行的时候都有一个与之对应的ThreadLocal对象属性,看到这里我们大致可以明白为什么会说ThreadLocal是一个线程变量了,但是JDK中到底是如何做到将ThreadLocal设置为一个线程变量的呢,看这个属性的注释,这个变量由ThreadLocal维护,我们就知道答案在ThreadLocal中。

我们看下ThreadLocal中是如何实现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)
                return (T)e.value;
        }
        return setInitialValue();
    }

/**
     * 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;
    }

get方法首先获取当前线程的threadLocals变量,这个变量在线程初始化时是空的, 所有在线程中第一次调用ThreadLocal对象的get方法会直接调用setInitialValue方法,setInitialValue方法实现如下:

/**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    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;
    }

这段代码就是将initialValue方法的返回值设置到当前线程,最后返回这个值,但是这个方法的返回值是null。回到我的项目中,由于ThreadLocal对象是直接创建的,所以在后面通过ThreadLocal的get方法都只会等到一个空值。

如果需要ThreadLocal对象有默认初始值,我们只需要重写这个方法就可以了。

//O为自定义类
ThreadLocal<O> threadLocal = new ThreadLocal<O>(){

			public O initialValue(){
				return new O();
			}
		};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值