一.ThreadLocal的含义
ThreadLocal也就是线程本地变量,创建了一个ThreadLocal变量,访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际上是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。
public static void main(String[] args) {
ThreadLocal<Integer>local=new ThreadLocal<>();
Thread thread1=new Thread(()->{
System.out.println(local.get());
local.set(1);
System.out.println(local.get());
});
Thread thread2=new Thread(()->{
System.out.println(local.get());
local.set(2);
System.out.println(local.get());
});
thread1.start();
thread2.start();
}
我们看到thread1对local变量值的修改并没有影响thread2线程的local变量
线程thread1和线程thread2从主内存中分别读取一份数据作为副本到本地的工作内存,然后去使用这个变量,变量之间并不会相互影响
二.ThreadLocal底层结构
ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值
三.ThreadLocal发生内存泄露的原因,以及解决办法
我们先来看一段代码
public static final ThreadLocal<byte[]>local=new ThreadLocal<>();
public static void main(String[] args) {
ExecutorService service=Executors.newFixedThreadPool(5);
for(int i=0;i<100;i++)
{
service.execute(()->{
local.set(new byte[50240*10240]);
});
}
service.shutdown();
}
我们创建了一个线程池,去给这个local变量value值赋值,最后运行程序,我们发现栈溢出了
nhxi
那这又是因为什么呢?我们先来看一张图
我们的线程对象是通过强引用指向ThreadLocalMap,而ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也不会被gc回收,当Entry对象越来越多,就造成内存泄漏
那如何解决这个问题呢?只需要我们手动释放ThreadLocal对象就可以了。
public static final ThreadLocal<byte[]>local=new ThreadLocal<>();
public static void main(String[] args) {
ExecutorService service=Executors.newFixedThreadPool(5);
for(int i=0;i<100;i++)
{
service.execute(()->{
local.set(new byte[50240*10240]);
local.remove();//手动释放local对象
});
}
service.shutdown();
}
四.ThreadLocalMap的哈希冲突解决
ThreadLocalMap使用开放地址法来解决Hash冲突的问题,会将当前插入元素从冲突位置开始依次往后遍历,直到找到一个空闲的位置,然后把元素放在这个空闲位置
五.ThreadLocalMap扩容机制
ThreadLocalMap扩容机制和HashMap类似,也就是在元素数量达到阈值(通常为数组的3/4)时
进行扩容,在set()方法中,如果当前元素已经达到了阈值,就会调用rehash方法,rehash方法先会清理过期的Entry,然后判断size是否>=3/4threshold如果大于则进行扩容,然后进行重新哈希,此时新数组长度为原来的2倍
六.ThreadLocal如何实现父子线程通信
Thread类当中存在InheritableThreadLocal变量,也就是说使用InheritableThreadLocal来进行传递,当父线程的InheritableThreadLocal不为空时,就会将这个值传到当前子线程InheritableThre
dLocal
public static void main(String[] args) {
ThreadLocal<String>threadLocal=new ThreadLocal<>();
threadLocal.set("threadlocal");
ThreadLocal<String>inheritableThreadLocal=new InheritableThreadLocal<>();
inheritableThreadLocal.set("inheritableThreadLocal");
Thread thread=new Thread(()->{
System.out.println(threadLocal.get());
System.out.println(inheritableThreadLocal.get());
});
thread.start();
}