
本文共 1690 字,大约阅读时间需要 5 分钟。
亮明观点:
ThreadLocal其实不是用来解决多线程变量共享线程安全问题的。
易混淆点:
ThreadLocal也不是在其他线程中创建副本的(不是什么对象的拷贝或副本),而是内部直接创建new的,然后存储到当前线程的Map中用的时候直接取出,相当于私有变量的效果,那为什么这么设计呢,与之前jdk1.3相比设计大变,速度快了很多。
使用ThreadLocal的目的是共享变量而不是共享对象
ThreadLocal的功能是提供访问对象的方法和防止参数层层传递的麻烦。
例如:一个线程可能创建多个connection但是若使用ThreadLocal它内部只保留一个connection在ThreadLocalMap中,下一次使用,直接取即可,不用频繁创建避免了线程大量创建connection而导致的资源浪费。
注意:
如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
为什么key使用弱引用,内存泄漏是否是弱引用的锅?
下面我们分两种情况讨论:
(1)key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用(被数据绑定着),如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
例如:
ThreadLocal<Integer> count = new ThreadLocal<Integer>();
//使用count
count.set(10);
// count不再被使用,可以进行内存回收,但是是强引用,count不会被回收,除非手动删除设置count为null;
(2)key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收即key自动设置为null。value在下一次ThreadLocalMap调用set、get、remove的时候会被清除。
因此:内存泄漏归根结底是由于ThreadLocalMap的生命周期跟Thread一样长。存在着强引用链路,Thread->ThreadLocalMap->Entity->(key,value),如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
如何避免内存泄漏
每次使用完ThreadLocal,都调用它的remove()方法,清除数据。
注意:并不是所有使用ThreadLocal的地方,都在最后remove(),他们的生命周期可能是需要和项目的生存周期一样长的,所以要进行恰当的选择,以免出现业务逻辑错误!
总结:
ThreadPool的原理:其实就相当于操作系统线程的上下文,上下文也是存储线程信息的,不过在这里用ThreadLocal存储线程信息!
使用场景:上面描述了它的功能!
错误理解(被广泛认为是副本和地址分发,其实都不是)
错误用法(传入了对象,其实不应该是对象,传入对象可能导致并发安全问题)
正确理解(new了对象存入Map,下次直接取,除非你remove了,下次想用还需要new)
正确用法(传入变量而非对象,变量是new的)
内存泄漏(两种方式,key弱引用没有remove,一个是key是被static修饰;总结其实是一个原因,不需要的ThreadLocal没有被Map清除罢了)
弱引用与强引用key的区别和分别解决内存溢出的思路:上面有!
ThreadPool设置为Static延长生命周期问题和减少多实例化问题:尽量设置ThreadLcoal为static,毕竟好处还是很大的减少多实例化,节省了资源!
ThreadPool拆箱空指针问题:Long->long,integer->int,然后我们get的时候是null.get(),当然会出现空指针错误!
ThreadLocal传入了线程共享对象:使用并发安全工具
发表评论
最新留言
关于作者
