【面试问题】四个问题让你把ThreadLocal拿捏的死死的

大家好,我是被白菜拱的猪。
一个热爱学习废寝忘食头悬梁锥刺股,痴迷于girl的潇洒从容淡然coding handsome boy。

ThreadLocal

1、为什么需要ThreadLocal

多线程访问同一个共享变量时特别容易出现并发问题,特别是在多个线程需要对一个共享变量写入时。为了保证线程安全,一般使用者需要在访问共享变量时需要进行适当的同步,而同步的措施一般是加锁,这就需要使用这对锁有一定的了解,显然是加重了使用者的负担,那么有没有一种方式可以做到,当创建一个变量后,每个线程对其进行访问的时候访问的是自己线程的变量呢?ThreadLocal就是做这个事情。

ThreadLocal是JDK包提供的,提供了线程本地变量,也就是如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,当多个线程操作这个变量时,实际上操作的是自己本地内存里面的变量,从而避免了线程安全问题。

2、ThreadLocal具体的实现原理是什么呢?

在每个线程内部都有一个名为threadLocals的成员变量,该变量类型为ThreadLocalMap类型,而ThreadLocalMap实际上就是一个定制化的HashMap,其中key为我们定义的ThreadLocal变量的this引用,value则为我们使用set方法的值。

实际上ThreadLocal就是一个工具壳,并不存值,而是每个线程中的TheadLocalMap存具体的值,这个变量刚开始为null,只有当前线程第一次调用TheadLocal的set或者get才会创建他们。在set方法中,首先获取当前线程,然后获得threadLocals,假如为空则创建,不空则set值。

3、ThreadLocal会有什么问题呢?

假如当前线程一直不消亡,那么这些本地变量会一直存在,所以可能会造成内存泄漏,因此使用完毕后记得调用ThreadLocal的remove方法删除对应线程的threadLocals中的本地变量。

其实ThreadLocalMap的Entry中的key使用ThreadLocal对象的弱引用,这在避免内存泄漏方面是一个进步,因为如果是强引用,即使其他地方没有对ThreadLocal对象的引用,ThreadLocalMao中的ThreadLocal对象还是不会被回收,而如果是弱引用则ThreadLocal引用是会被回收掉的,但是value还是不能被回收,这时候就会出现key为null但是value为null的情况,但是比强引用好多了,另外在set、get、remove的时候能够对这个entry进行清理,但是这时不及时的,也不会每次都会执行,所以每次使用完之后及时调用remove才是解决内存泄漏问题的王道。

4、ThreadLocal的具体应用场景有哪些?

ThreadLocal在工作中使用的不太多,不过还是有的,我们项目中有个DateUtils工具类,这个工具类主要是对时间进行格式化,使用的是SimpleDateFormat。

为什么用到了TheadLocal,主要是因为SimpleDateFormat是线程不安全的。

为什么SimpleDateFormat是线程不安全的?

当多个线程使用用同一个sdf对象对字符串进行格式化的时候,会报 java.lang.NumberFormatException。

我们点开SimpleDateFormat这个类发现,每个实例里面都有一个Calendar对象,SimpleDateFormat之所以是线程不安全的,是因为Calendar是线程不安全的,Calendar不安全,是因为其中存放日期数据的变量是线程不安全的,比如fields、time等,当多个线程进行操作的时候,取值赋值这一系列的操作由于不是原子性,所以会导致线程不安全问题。

那怎么解决SimpleDateFormat线程不安全的问题呢?

  • 第一种方式,每次使用的时候都new一个SimpleDateFormat对象,这个可以保证每个线程使用自己的Calendar对象,但是使用过后,由于没有其他引用,又需要回收,开销很大。
  • 第二种方式,使用锁来保证原子性操作,这个意味着多个线程要竞争锁,在高并发场景下回导致系统响应性能下降。
  • 第三种方式,使用ThreadLocal,这样每个线程只需要使用一个SimpleDateFormat实例,相较于第一种方式大大节省了对象的创建销毁开销,并且不需要使多个线程同步。

请问还有其他ThreadLocal的应用场景吗?

有的,在Spring框架中,Spring提供了事务的相关操作,而我们知道事务是得保证一组操作同时成功同时失败的,这就意味着我们一次事务的所有操作需要在同一个数据库连接上,那么怎么保证呢?

Spring就是用的ThreadLocal来实现的,ThreadLocal存储的类型是一个Map,Map中的key是DataSource,value是Connction,为了应对多个数据源的情况,所以是一个Map,用ThreadLocal保证了同一个线程获取一个Connection对象,从而保证一次事务的所有操作都需要在同一个数据库连接上。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值