Java并发:网友对ThreadLocal的理解误区和知识重点!

亮明观点:

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传入了线程共享对象:使用并发安全工具

猜你喜欢

转载自blog.csdn.net/StrawberryMuMu/article/details/106404956