ThreadLocal之我所见

网上有不少关于ThreadLocal的文章,大部分都提到了多线程之间共享资源的问题。其实ThreadLocal和多线程之间一点关系都没有。若是有,我怕是它的名字改为ThreadShare是否是更合适呢?开个玩笑。从其名称ThreadLocal,咱们就能够看出他应该是隶属于线程内部的资源。接下来就详细说说吧。html

咱们之前在处理一个Request请求,穿透多个业务方法的时候,若是要共享数据,通常都会搞个Context上下文对象,在多个方法之间进行传递,这样能够解决多个方法之间的数据共享问题。这是很不错的一种方案,并且这种方案在实际使用的时候效果也很是不错,由于Context内部咱们想放什么,就放什么,想怎么放就怎么放,很是的自由无界。可是这种自由带来的问题在小流量的请求中是不会有问题的,可是在大流量的请求中,则存在不小的问题,主要在:安全

1. Context对象,每一个请求进来,都会new一个,大流量下,瞬间暴增,因为空间申请操做势必引起频繁的young GC, 业务压力大的时候,full GC也是不可避免的。多线程

2. Context对象,在一个请求终结以后,须要手动释放。ide

3. Context对象,存在被请求内部的多线程共享访问的情形。有线程安全性问题。this

上面三个问题,是我随便列举的,可是在实际使用中,可能还有更多。可是若是Context的生产和销毁若是控制的足够好的话,上面的问题也不是什么问题。spa

既然Context对象的控制稍显麻烦,那么JDK有没有提供什么现成的类库供咱们使用呢? 答案是确定的,这个对象就是ThreadLocal对象。线程

说道ThreadLocal对象,我更认为他是在当前请求的上游和下游之间进行数据共享的。那么按照以前的例子说来,若是一个请求穿越多个业务方法,其实数据的共享能够利用ThreadLocal来进行,这样就无需专门定义一个Context。code

首先来看看其内部get实现机制:htm

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

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

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

protected T initialValue() {
    return null;
}

从上面源码能够看出,get操做会从当前Thread上附加的map中进行数据获取。对象

再来看看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);
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

从上面源码看出,首先他会取出当前Thread,而后会将map设置到当前Thread上,用户能够在这个map上进行数据增删改查操做。很是巧妙。因此从这里能够看出,一个ThreadLocal对象,即使穿插在多个线程之间,也不会形成资源共享问题,由于他会为每一个线程都设置map对象,这也从根本上避免了线程安全问题。

最后,由于ThreadLocal内部的对象为WeakReference,因此不用进行手动释放,只须要保证一个请求结束,对其内部的引用释放掉就好了,而后自动会被JVM优先处理掉,根本无需担忧内存泄露问题。

相关文章
相关标签/搜索