面试题【ThreadLocal】

总述

ThreadLoca不是Thread,是一个线程内部的数据存储类。它可以在指定的线程中存储数据,并且只有在此线程中才可以获取到存储到数据,其他线程是无法获取到的。(提供了线程独有的变量)

demo

public class ThreadLocalTest {

    public static void main(String[] args) {
        final ThreadLocal threadLocal1 = new ThreadLocal<>();
        final ThreadLocal threadLocal2 = new ThreadLocal<>();

        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal1.set("A");
                threadLocal2.set(1);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
                System.out.println(threadLocal1.get());
                System.out.println(threadLocal2.get());
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal1.set("B");
                threadLocal2.set(2);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
                System.out.println(threadLocal1.get());
                System.out.println(threadLocal2.get());
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                System.out.println(threadLocal1.get());
                System.out.println(threadLocal2.get());
            }
        }).start();
    }
}

结果如下:

Thread-2
null
null
Thread-1
B
2
Thread-0
A
1

可以看出,虽然在不同线程中访问的是同一个ThreadLocal对象,但是它们通过ThreadLocal来获取到的值却是不一样的,这就是ThreadLocal的奇妙之处。

内部实现

ThreadLocal使用的是泛型。

ThreadLocalMap就是用来存储的内部类。其内部是一个Entry[] table数组,存储所有数据,Entry对象的key为ThreadLocal,value为其对应的值。数组默认大小是16。

当ThreadLocal使用set方法时,会先获取当前线程,然后调用getMap(currentThread),获取线程中的数据存储类ThreadLocalMap。如果ThreadLocalMap不为空,就调用set方法进行赋值。如果为空,就进行初始化,并将数据设置

这样,每一个Thread中,都保留着自己的一份ThreadLocalMap数据存储类,数据独享。

使用get方法时,会先获取当前线程,然后调用getMap(currentThread),获取线程中的数据存储类ThreadLocalMap,再进行取值

 

在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)

内存泄漏

key为弱引用。弱引用一定程度上回收了无用对象,但前提是开发者手动清理掉ThreadLocal对象的强引用。

回收之后成了(null, value)

只要线程一直不死,ThreadLocalMap的key-value一直在涨

如果线程执行完之后销毁,value也会被回收,这样也没问题。但如果是在线程池中,线程执行完后不被回收,而是返回线程池中,Thread有个强引用指向ThreadLocalMap,ThreadLocalMap有强引用指向Entry,导致value无法被回收,一直存在内存中。在执行了ThreadLocal.set()方法之后一定要记得使用ThreadLocal.remove(),将不要的数据移除掉,避免内存泄漏。


原文链接:https://blog.csdn.net/wy1404081737/article/details/81489080

 

你可能感兴趣的:(面试题)