ThreadLocal

对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量(实际上,这项工作需要程序员自己实现),因此可以同时访问而互不影响。

比如,要让100个人填一张表,如果只有一支笔。对于管理人员必须保证大家不会哄抢这一支笔,否则,大家都填不完。从另一个角度,我们可以准备100支笔,人手一支笔,这样就可以各自为营互不干扰。锁使用的是第一种思路,ThreadLocal使用的是第二种思路。

注意:为每一个线程分配一个对象的工作不是由ThreadLocal来完成的,而是需要在应用层面来实现(即程序员代码实现)。如果在应用上为每一个线程分配了相同的对象实例,那么ThreadLocal也不能保证线程安全。简言之,ThreadLocal只是起到了简单是容器作用,为每一个线程分配不同的对象,需要在应用层面保证。

ThreadLocal的实现原理

ThreadLocal中有四个主要的方法:get,set,remove,initialValue,下面主要讨论前两种。

ThreadLocal类中有一个静态内部类ThreadLocalMap(可以认为这是一个map),但是ThreadLocalMap的实例threadlocals却在Thread类中。

从源码中可知,每个线程都有一个ThreadLocalMap的实例threadlocals。在set方法中首先通过当前线程,拿到当前线程的ThreadLocalMap对象。Key为当前ThreadLocal对象,value就是需要并发操作的对象。(至于为什么用map存储数据,而不用list或其他单列集合是因为单列集合的底层是由map来实现的)。实际上,设置到ThreadLocal中的数据,其实是保存在当前线程的ThreadLocalMap这个Map中。

get方法就是先获取当前线程的ThreadLocalMap对象,然后将自己作为Key取得内部的实际数据。

ThreadLocal的回收机制

提出问题:这些变量是维护在Thread类内部的threadLocals中,这也意味着只要线程不退出,对象的引用将一直存在。如果我们使用线程池,那就意味着当前线程未必会退出(比如固定大小的线程池,线程总是存在)。如果这样,将一些太大的对象设置到ThreadLocal中(实际是保存在线程持有的ThreadLocals Map中),此时可能会出现内存泄漏。

(1)如果希望及时回收对象,可以使用ThreadLocal.remove()方法将这个变量移除。就像我们习惯性的关闭数据库连接一样。

(2)还有一种方式,对于ThreadLocal变量,我们可以手动的将其置为Null,比如tl =null。那么这个ThreadLocal对应的所有线程的局部变量都有可能被回收。

ThreadLocalMap是一个类似HashMap的东西,更准确的说,它更加类似WeakHashMap。ThreadLocalMap的实现使用了弱引用。弱引用是比强引用弱的多的引用。Java虚拟机在垃圾回收时,如果发现弱引用,就会立即回收。ThreadLocalMap内部由一系列Entry构成,每一个Entry都是WeakReference<ThreadLocal>

ThreadLocal的应用场景

Spring数据库连接:在多线程中,如果使用懒汉式的单例模式创建Connection对象,由于该对象是共享的,那么必须要使用同步方法保证线程安全,这样当一个线程在连接数据库时,那么另外一个线程只能等待。这样就造成性能降低。如果改为哪里要连接数据库就来进行连接,那么就会频繁的对数据库进行连接,性能还是不高。这时使用ThreadLocal就可以既可以保证线程安全又可以让性能不会太低。但是ThreadLocal的缺点时占用了较多的空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值