一,ThreadLocal与Synchonized区别
ThreadLocal<T>其实是与线程绑定的一个变量。ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享
可以看下threadLocal.get()方法的源码
每个线程都会有根据当前线程对应的map,取值的时候yehu也会从这个副本中取,所以每个线程的threadLocal是相互隔离的
package com.qingwei.springboot.juc;
/**
* Created by kongwc on 2020/3/19.
*/
public class ThreadLocalDemo {
private static ThreadLocal<Integer> total = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue(){
return 1;
}
};
public static void startLocalThread(){
Thread[] threads = new Thread[5];
for(int i=0; i<threads.length; i++){
threads[i] = new Thread(new MyThread(i));
}
for(int i=0; i<threads.length; i++){
threads[i].start();
}
}
static class MyThread implements Runnable{
private Integer index;
MyThread(Integer i){
this.index = i;
}
@Override
public void run() {
index = index + total.get();
total.set(index);
System.out.println("当前的总数为:" + total.get() + "----当前线程名称:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
startLocalThread();
}
}
二、ThreadLocalsh使用不当引起内存泄漏问题
在ThreadLocal.get()方法源码中一直往下看会发现yo有个WeakReference,此处表示弱引用,java中的引用分为四种,按照引用强度不同,从强到弱依次为:强引用、软引用、弱引用和虚引用,如果是弱引用,每次垃圾回收的时候都会回收掉,
由于ThreadLocalMap是以弱引用的方式引用着ThreadLocal,换句话说,就是ThreadLocal是被ThreadLocalMap以弱引用的方式关联着,因此如果ThreadLocal没有被ThreadLocalMap以外的对象引用,则在下一次GC的时候,ThreadLocal实例就会被回收,那么此时ThreadLocalMap里的一组KV的K就是null了,因此在没有额外操作的情况下,此处的V便不会被外部访问到,而且只要Thread实例一直存在,Thread实例就强引用着ThreadLocalMap,因此ThreadLocalMap就不会被回收,那么这里K为null的V就一直占用着内存。
三. ThreadLocal使用建议
通过前面几节的分析,我们基本弄清楚了ThreadLocal相关设计和内存模型,对于是否会发生内存泄露做了分析,下面总结下几点建议:
- 当需要存储线程私有变量的时候,可以考虑使用ThreadLocal来实现
- 当需要实现线程安全的变量时,可以考虑使用ThreadLocal来实现
- 当需要减少线程资源竞争的时候,可以考虑使用ThreadLocal来实现
- 注意Thread实例和ThreadLocal实例的生存周期,因为他们直接关联着存储数据的生命周期
- 如果频繁的在线程中new ThreadLocal对象,在使用结束时,最好调用ThreadLocal.remove来释放其value的引用,避免在ThreadLocal被回收时value无法被访问却又占用着内存