欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ThreadLocal使用场景

程序员文章站 2024-02-16 09:31:16
...
ThreadLocal是什么?

ThreadLocal是用来维护线程中的变量不被其他线程干扰而出现的一个结构,内部包含一个ThreadLocalMap类,

该类为Thread类的一个局部变量,

该Map存储的key为ThreadLocal对象自身,value为我们要存储的对象,这样一来,在不同线程中,持有的其实都是当前线程

的变量副本,与其他线程完全隔离,以此来保证线程执行过程中不受其他线程的影响。


ThreadLocal结构:

ThreadLocal使用场景 
主要是四个方法: 
1. void set(Object value) 
设置当前线程的线程局部变量的值。 
2. public Object get() 
该方法返回当前线程所对应的线程局部变量。 
3. public void remove() 
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。 
4. protected Object initialValue() 
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

我们看下set方法的实现:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看到,先通过Thread.currentThread()方法获取到了当前线程,然后如果取不到map对象,就会创建,下面看下create方法

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
  • 1
  • 2
  • 3

很简单的方法,就是新建一个ThreadLocal的Map,这个map以ThreadLocal自身为key,以我们要设值的对象为value,创建出来map之后,将对象赋值到线程的局部变量去。 
看到这里,就知道ThreadLocal主要目的就是将变量设值到当前的线程上,以此来保证线程安全。 
那么下面看下get方法:

    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();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以看出来,get方法就是拿的当前线程的局部变量threadLocals,然后从中取出map中存储的对象,这样每个线程中获取的一定是自己线程中存储的对象了。 
由上述分析可知,使用ThreadLocal是可以保证线程安全的。 
ThreadLocal实际上是为解决多线程程序的并发问题提供了一种新的思路(区别与sychronized关键字)。 
ThreadLocal这个类提供线程本地的变量。这些变量与一般正常的变量不同,它们在每个线程中都是独立的。ThreadLocal实例最典型的运用就是在类的私有静态变量中定义,并与线程关联。 
我们看下具体的使用场景吧:

  • 我们知道SimpleDateFormat是非线程安全的,并发场景下会出现格式化时间错误的问题,那么在这里我们就可以使用ThreadLocal来解决此类问题。
    private static ThreadLocal<SimpleDateFormat> sdt = new ThreadLocal<SimpleDateFormat>();

    public static final SimpleDateFormat getSdt() {
        if (null == sdt.get()) {
            sdt.set(new SimpleDateFormat("MM月dd日HH:mm"));
        }
        return sdt.get();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 看另外一个场景,一个请求过来,会带有一堆的c参数(可以理解为客户端的一些标志),我们的应用处理过程中,大部分地方又不需要关心该参数,可能在某个请求他人接口的时候需要了,如果我们把所有代码都带上这个c参数,那么未免代码看着太过丑陋,这种情况下,我们可以构建一个filter,在请求过来的时候,在filter中将c参数放置到ThreadLocal中,在整个调用链中如果需要使用,直接从ThreadLocal中获取即可。 
    ThreadLocal使用场景
  • 另外一个例子是动态数据源的使用,我们可以使用ThreadLocal来保证当次线程调用中只使用一种数据源。这部分内容下个文章我会单独列出来讲解一下。

最后我们在提一点,就是使用ThreadLocal是否会造成内存泄漏,这一点答出来相信应该会给自己很多加分吧~ 
这个是ThreadLocal自身的注释:

每一个线程对资源副本都有一个weekRefrence:只要线程还在运行,使用ThreadLocal就是可以获取的。当一个线程运行结束销毁时,所有的资源副本都是可以被垃圾回收的(ThreadLocalMap对象保存在Thread对象中,当某个线程终止后,存储在其中的线程隔离的变量,也将作为Thread实例的垃圾被回收掉,所以完全不用担心内存泄漏的问题),这段注释表明,ThreadLocal的使用是不会造成内训泄露的。

相关标签: thread