ThreadLocal使用

245 阅读3分钟

更新中······

是什么

简单来说:
1、 ThreadLocal提供了一种定义线程的局部变量的方式
2、这个局部变量在多个线程中拥有不同的副本

特点:

  • 简单(使用简单)
  • 快速(无额外开销)
  • 安全(线程安全)


初步认识:

  • 构造函数ThreadLocal()
  • 初始化 intialValue()
  • 访问器 get()/set()
  • 回收 remove()

这个类很简单,主要就这么几个方法,顾名思义,很容易看懂每个方法什么意思。

  • 1、ThreadLocal提供了一种访问某个变量的特殊方式,访问到的变量属于当前线程,即保证每个线程的变量都不一样,二同一个线程在任何地方拿到的变量都是一致的,这就是所谓的线程隔离。
  • 2、如果要使用ThreadLocal,通常定义为private static类型

解决什么问题

  • 资源持有
  • 线程一致性
  • 并发计算
  • 线程安全


当你想在多个方法中使用某个变量,这个变量是当前线程的状态,其他线程都不不依赖这个变量
我们通常会把这个变量定义在方法内部,然后在方法之间通过参数传递使用。
这个方法能解决问题,但是

  • 每个需要用到这个变量的方法,都需要声明形参
  • 如果引用的方法比较多,层次比较深,会非常影响代码的美观,并且增加维护成本。
  • 而且,如果这些方法都是已定义好的,现在需要增加一个新的变量,你就需要去修改每个方法的形参,以及调用这个方法的地方。

怎么用

这时候ThreadLocal就派上用场了,请看代码:

//ThreadLocal
 private static ThreadLocal<String> holder = new ThreadLocal<>();

//嵌套方法a
public void doA(){
    String param = holder.get();
	doB();
}

//嵌套方法b
public void doB(){
    String param = holder.get();
	doC();
}

//嵌套方法c
public void doC(){
    String param = holder.get();
}


public static void main(String[] args) {
    //绑定参数到当前线程
    String name = "123";
    holder.set(name);
    tyr(){
        doA();
    }finanly{
        //最后清理掉,防止内存泄漏(为什么?后面原理中有说明)
    	holder.clear();      
    }
}


如果只是想知道怎么用,看到这里就够了,如果想深入了解,请继续往下看。

原理解析

Java中使用 哈希表 实现

源码解析

首先来看看看源码
ThreadLocal中有三个方法:分别是 get()set(T value)remove()

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

继承性

ThreadLocal 定义的变量不具有继承性,(父线程定义的变量,子线程中是访问不到的)
InheritableThreadLocal 顾名思义,这个类是支持子线程访问父线程定义的变量的。
请看代码:

public class Test {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        String mainvalue = "mainValue";
        threadLocal.set(mainvalue);
        inheritableThreadLocal.set(mainvalue);
        //打印主线程的值
        System.out.println(Thread.currentThread().getName() + "【threadLocal-父线程访问】" + threadLocal.get());
        System.out.println(Thread.currentThread().getName() + "【inheritableThreadLocal-父线程访问】" + threadLocal.get());
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                //打印子线程的值
                System.out.println(Thread.currentThread().getName() + "【threadLocal-子线程访问】:" + threadLocal.get());
                System.out.println(Thread.currentThread().getName() + "【inheritableThreadLocal-子线程访问】" + inheritableThreadLocal.get());
            }
        });
        thread.start();
    }
}

控制台输出结果:

main【threadLocal-父线程访问】mainValue
main【inheritableThreadLocal-父线程访问】mainValue
Thread-0【threadLocal-子线程访问】:null
Thread-0【inheritableThreadLocal-子线程访问】mainValue

从上面的代码可以看出

  • ThreadLocal父线程中绑定的变量,子线程中是获取不到的。
  • InheritableThreadLocal父线程绑定的变量,子线程是可以获取到的。

内存泄漏问题