网上有不少关于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优先处理掉,根本无需担忧内存泄露问题。