ThreadLocal解析
synchronized和ThreadLocal的区别:
synchronized:以时间换空间,只提供一份变量,让不同的线程排队访问,失去了并发性,降低了程序效率,着重对各线程之间访问资源的同步性
ThreadLocal:以空间换时间,每个线程都提供一份变量副本,每个线程的数据是隔离的,提高了并发性
ThreadLocal使用场景:数据库连接、Session管理
ThreadLocal的数据结构:
Mapnew ThreadLocal(),T>> map=new HashMap<>();
方便理解,源码中并没有这个map
set()方法:
getMap():
createMap():
Ok现在我们走一遍程序流程:1.现在有一个ThreadLocal实例对象->ThreadLocalconn_tl = new ThreadLocal()
调用conn_tl.set()方法,获取当前执行的线程,根据当前线程获取一个ThreadLocalMap实例对象threadLocals,threadLocals里面存 conn_tl和value
ThreadLocalMap是一个定义在ThreadLocal中的静态内部类:
static class ThreadLocalMap {
static class Entry extends WeakReference> {
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);//由于Entry继承了WeakReference,所以这里以一个弱引用指向ThreadLcoal对象
value = v;
}
}
为什么ThreadLocalMap会是ThreadLocal的静态内部类:ThreadLocalMap是由ThreadLocal创建管理的,只被ThreadLocal所使用
静态内部类的作用:1)首先,用内部类是因为内部类与所在外部类有一定的关系,往往只有该外部类调用此内部类。所以没有必要专门用一个Java文件存 放这个类。
2)静态都是用来修饰类的内部成员的。比如静态方法,静态成员变量,静态常量。它唯一的作用就是随着类的加载(而不是随着对象的产生)而产生,以致可以用类名+静态成员名直接获得。
这样静态内部类就可以理解了,因为这个类没有必要单独存放一个文件,它一般来说只被所在外部类使用。并且它可以直接被用外部类名+内部类名获得。
为什么Entry中的key是弱引用:
这里的key是ThreadLocalconn_tl = new ThreadLocal()中的conn_tl。在外部他是一个强引用,在Entry中key是弱引用。也就是有两个引用指向ThreadLocal实例对象
当外部方法执行完毕,栈帧销毁强引用tl也就也就销毁了,但此时线程的ThreadLocalMap里某个entry的 k 引用还指向这个对象。若这个k 引用是强引用,就会导致k指向的ThreadLocal对象及v指向的对象不能被gc回收,造成内存泄漏,但是弱引用就不会有这个问题(弱引用及强引用等这里不说了)。使用弱引用,就可以使ThreadLocal对象在方法执行完毕后顺利被回收,而且在entry的k引用为null后,再调用get,set或remove方法时,就会尝试删除key为null的entry,可以释放value对象所占用的内存。
概括说就是:在方法中新建一个ThreadLocal对象,就有一个强引用指向它,在调用set()后,线程的ThreadLocalMap对象里的Entry对象又有一个引用 k 指向它。如果后面这个引用 k 是强引用就会使方法执行完,栈帧中的强引用销毁了,对象还不能回收,造成严重的内存泄露。
弱引用的好处:当一个对象仅仅被weak reference指向, 而没有任何其他strong reference指向的时候, 如果GC运行, 那么这个对象就会被回收。如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象。