threadlocal存连接对象的目的_浅析ThreadLocal

对于刚接触多线程的初学者来说,会发现很多框架都出现过ThreadLocal的身影,知道这个类是为了避免在多线程条件下出现资源竞争的问题。但是,在没有深入了解线程之前,更多的是知道ThreadLocal的目的是什么,对于是什么、怎么用、运行原理相信都是一个很模糊的概念,我希望这篇博客能够揭开困扰大家已久的谜团,以及带给大家一些相关知识。

说到要分析东西的时候,我觉得有一个比较好的方法可以帮助大家:不管分析什么,框架也好、源码也好,首先要对这个东西有一个概况了解、知道它是做什么的。千万不要一上来就看的很细很细,这样容易把自己绕晕而且效率很低(血的教训,哭);如果是分析JDK源码,尤其是集合框架的时候,可以先弄懂它的底层数据结构是什么,把底层数据结构弄懂了,那么分析这个类只是水到渠成的事情。哈哈

实现原理

其实就像我上面所说的那样,只要对线程有了一个深入的了解认识以后,就会很容易的理解ThreadLocal。

我们先用通俗的话解释一下ThreadLocal是一个什么东西,一句话:通过ThreadLocal保存的对象将在每一个使用它的线程留存一个副本,从而避免资源竞争,以空间换时间。

我们很自然的会有这样的疑问:

ThreadLocal是如何将对象保存在各个线程中的呢?

ThreadLocal又是如何获取保存在线程本地的对象的呢?

其实,我们可以这样猜:Thread应该有一个Map,保存了ThreadLocal维护的对象。

事实上ThreadLcoal和Thread也是这么做的:

Thread类持有了ThreadLcoal类中有一个内部类ThreadLocalMap,通过将对象保存在这个Map中来实现每个线程都持有一个对象副本;

以ThreadLocal为Key,进行Key/Value存取操作。

ThreadLocal.java

//我们可以清晰看到get方法的逻辑:1、通过当前线程获取ThreadLocalMap;2、通过this(ThreadLocal)获取线程本地对象

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();

}

//set方法的逻辑很简单,如果map存在则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);

}

static class ThreadLocalMap{

...

...

...

static class Entry extends WeakReference> {

/** The value associated with this ThreadLocal. */

Object value;

Entry(ThreadLocal> k, Object v) {

super(k);

value = v;

}

}

}

Thread类中则持有了这个map,

Thread.java

ThreadLocal.ThreadLocalMap threadLocals = null;

如何工作

我们最关心的还是如何使用ThreadLocal,与使用息息相关的就是set()和get()方法,我们把上面的代码拿过来再用一下:

public T get() {

//获取当前线程,用于获取ThreadLocalMap

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;

}

}

//如果为map == null,则执行setInitialValue()方法,进行初始化

return setInitialValue();

}

//设置初始值,逻辑与get()方法类似

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方法,子类必须实现这个方法以生成副本

protected T initialValue() {

return null;

}

//set方法的逻辑很简单,如果map存在则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);

}

代码中的注释应该比较详细,这里就不写了~

如果有不懂的,欢迎留言~

WeakReference

WeakReference是干啥的

细心的同学肯定发现了ThreadLocalMap的内部类Entry继承了WeakReference:

static class Entry extends WeakReference>

免不了想问WeakReference是干啥的?

我们可以很容易联想到Java中的四种引用关系,而WeakReference对应的是弱引用关系;

我们来看WeakReference的doc中第一句话,

Weak reference objects, which do not prevent their referents from being

made finalizable, finalized, and then reclaimed.

弱引用不会阻止它引用的对象变成finalizable、finalized状态并最终被回收

那,Entry为什么要继承WeakReference呢?

Entry为什么要继承WeakReference

如果线程太多(如线程池中的线程),那么保存的副本将会很多,而且不能回收。

因为thread没有运行结束那么会一直存在不能被回收,而threadLocals与对象副本之间是强引用的关系,所以这些副本也就不会被回收。大多数thread都是在阻塞等待状态,真正运行的thread只是少数,所以这些阻塞等待状态的thread所保存的副本就白白浪费了宝贵的内存资源。为了解决这个问题,JDK让Entry类继承了WeakReference类,这样当JVM进行GC的时候可以回收Thread保存的副本。这就意味着,副本对象必须是易于创建的,状态简单的对象,如数据库连接Connection。

Java中的四种引用

强引用

只要强引用的存在,垃圾回收器永远不会回收这个对象;

软引用(SoftReference)

当内存不足时,垃圾回收器将会回收被软引用引用的对象;

弱引用(WeakReference)

垃圾回收器将会在下一次GC时回收被弱引用引用的对象

虚引用(PhantomReference)

虚引用的存在与否完全不会影响垃圾回收器回收这个对象,虚引用存在的意义是对象被回收时发出一个通知。

扩展阅读

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值