参考:https://www.zhihu.com/question/657130561/answer/3578606797

  • 读这篇文章,延伸思考了一下,记录一下
  • 为什么要用ThreadLocal:一个是SimpleDateFormat的场景,如果它直接放到成员变量里,多线程同时使用它会有问题,于是就需要一个线程自己的SimpleDateFormat,且该SimpleDateFormat不和其他线程共享.ThreadLocal就因此被设计出来了.
  • 为什么用ThreadLocal做key,不用Thread作为key:Thread内部的map,key是Thread,Entry的key都是当前Thread,这个逻辑不对.如果这个ThreadLocalMap是一个静态的,那么Entry的value就得是一个ConcurrentHashMap,ConcurrentHashMap的key就是Class或String,value就必须是Object,然后使用的时候就类似这样(Xxx)ThreadLocalMap.getThreadLocals().get(Xxx.class),要强转,写法麻烦.
  • Entry的key为什么设计成弱引用:为了尽可能得回收内存.假设使用线程池,并且代码里有多个ThreadLocal,假设提交的任务有a,b,c三个,这三个任务对象里都有3个ThreadLocal(成员变量),那么执行完a任务之后,a对象被回收,a中的ThreadLocal对象也被回收,但是Entry对象没有被回收,因为entry是强引用,此时这个线程(和执行a任务的线程是同一个线程)再执行b任务,就会触发b中的ThreadLocal.set(T)/get(),而这两个API都会检测key为null(已经因弱引用被回收)的Entry,并将该Entry回收(如果有别的强引用持有该ThreadLocal,key不会为null).因此 ,设计成弱引用能尽可能回收内存.
  • Entry的value为什么不设计成弱引用:这次我们使用数据库中的事务的案例,这种案例中,ThreadLocal被设计成static,也就是强引用一直存在,假设TransactionUtil.begin()的方法中,就往当前线程里,以该static的ThreadLocal为key,映射了一个Transaction对象,如果这个Transaction是弱引用,那么,只要出了TransactionUtil.begin()这个方法,这个Transaction对象就有可能被回收变成null,那就没办法用了.

站在巨人的肩上
2 声望0 粉丝