ThreadLocal原理剖析

一.ThreadLocal的含义

ThreadLocal也就是线程本地变量,创建了一个ThreadLocal变量,访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际上是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。

  public static void main(String[] args) {
        ThreadLocallocal=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变量

ThreadLocal原理剖析_第1张图片 

线程thread1和线程thread2从主内存中分别读取一份数据作为副本到本地的工作内存,然后去使用这个变量,变量之间并不会相互影响

二.ThreadLocal底层结构

ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值

ThreadLocal原理剖析_第2张图片

ThreadLocal原理剖析_第3张图片

三.ThreadLocal发生内存泄露的原因,以及解决办法

我们先来看一段代码

  public static final ThreadLocallocal=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 

那这又是因为什么呢?我们先来看一张图

ThreadLocal原理剖析_第4张图片 

 我们的线程对象是通过强引用指向ThreadLocalMap,而ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也不会被gc回收,当Entry对象越来越多,就造成内存泄漏

那如何解决这个问题呢?只需要我们手动释放ThreadLocal对象就可以了。

  public static final ThreadLocallocal=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冲突的问题,会将当前插入元素从冲突位置开始依次往后遍历,直到找到一个空闲的位置,然后把元素放在这个空闲位置

ThreadLocal原理剖析_第5张图片

五.ThreadLocalMap扩容机制

ThreadLocalMap扩容机制和HashMap类似,也就是在元素数量达到阈值(通常为数组的3/4)时 

进行扩容,在set()方法中,如果当前元素已经达到了阈值,就会调用rehash方法,rehash方法先会清理过期的Entry,然后判断size是否>=3/4threshold如果大于则进行扩容,然后进行重新哈希,此时新数组长度为原来的2倍

ThreadLocal原理剖析_第6张图片

ThreadLocal原理剖析_第7张图片 

 

六.ThreadLocal如何实现父子线程通信

Thread类当中存在InheritableThreadLocal变量,也就是说使用InheritableThreadLocal来进行传递,当父线程的InheritableThreadLocal不为空时,就会将这个值传到当前子线程InheritableThre 

dLocal

public static void main(String[] args) {
        ThreadLocalthreadLocal=new ThreadLocal<>();
        threadLocal.set("threadlocal");
        ThreadLocalinheritableThreadLocal=new InheritableThreadLocal<>();
        inheritableThreadLocal.set("inheritableThreadLocal");
        Thread thread=new Thread(()->{
            System.out.println(threadLocal.get());
            System.out.println(inheritableThreadLocal.get());
        });
        thread.start();
    }

你可能感兴趣的:(java,开发语言)