Threadlocal的简单使用和实现原理解析

ThreadLocal常用来在一个线程的作用范围内共享变量,它在多个线程之间是相互独立的。

一、ThreadLocal的使用

下边的代码先声明了一个公共的ThreadLocal,泛型是string表示这个threadLocal中只能存string.

在线程t1中调用set方法给local赋值t1,然后线程t1分别去调用方法m1,m2,在m1,m2中用get方法获取local

中的值,在线程t2中也进行同样的操作。

然后就会得到这样的输出:t1线程调用方法时拿到的是t1自己设置的值,t2也是

t1-m2中获取local:t1
t1-m1中获取local:t1
t2-m2中获取local:t2
t2-m1中获取local:t2

这说明了2个线程在调用m1、m2方法的时候获取到的是自己的threadlocal中维护的值,互不干扰,所以threadlocal可以用来在一个线程范围内的多个方法间共享变量。


public class ThreadLocalTest {
    public static ThreadLocal<String> local = new ThreadLocal<>();

    public static void main(String[] args) {

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                local.set("t1");
                m1();
                m2();
            }
        },"t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                local.set("t2");
                m1();
                m2();
            }
        },"t2");
        t2.start();
    }

    public static void m1(){
        System.out.println(Thread.currentThread().getName()+"-m2中获取local:"+local.get());
    }

    public static void m2(){
        System.out.println(Thread.currentThread().getName()+"-m1中获取local:"+local.get());
    }

threadlocal就是这样使用的,先定义一个公共的变量,然后在线程中先设置值再获取值,使用这种方式可以减少通过方法传参来传递参数。

二、ThreadLocal的实现原理解析

ThreadLocal为什么可以实现线程独立呢,那是因为每个线程都维护自己的threadlocal,Thread类中有一个属性threadLocals,这个属性中会保存自己的变量值。

可以用一幅图来描述下上边的代码对应的堆内存。

由这张图可以看出来threadlocal为什么可以做到线程独立,因为每个线程对象中都有一个threadLocals属性,而ThreadLocal其实就相当于是一个工具类,set/get方法用来操作线程对象中的threadLocals属性,因为每个thread对象是独立的所以其属性也是独立的,就不会相互影响。

然后来简单看下ThreadLocal的源码,主要看下set/get方法

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);
    }

get方法

ublic 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();
    }

可以看到和上面的图是一致的。

三、子线程中能否获取到父线程中set的ThreadLocal值

如果使用的是ThreadLocal,子线程是不能访问的,因为线程对象(Thread)的threadLocals属性不会传递给子线程。

但Thread类中有另一个属性是会传递给子线程的

 ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

那么子线程就没办法和父线程共享ThreadLocal变量了吗?其实是可以的

ThreadLocal有一个子类InheritableThreadLocal就是用来操作线程对象的这个属性的,所以如果父线程中是给这个类对象set值,在子线程中是可以访问到的。