ThreadLocal原理解析

当我们在使用多个线程访问一个共享变量的时候特别容易带来数据安全问题,尤其是我们在对一个共享变量写操作的时候,我们通常的解决方案是加锁,使对该共享变量的操作串行化,那么有没有一种方式可以做到,创建一个变量,每个线程对其访问的时候访问的是自己线程变量呢?使用ThreadLocal就可以实现。
ThreadLocal它提供了一个本地线程变量,通俗的讲就是你创建了一个ThreadLocal变量,线程a在给该变量set值value的时候,实际上这个value被保存到a线程中的一个map中了,key就是ThreadLocal这个变量,value就是你set的value。实际上你每个线程操作的这个变量都是在操作自己线程内存里面的变量,各个线程之间互不影响。

ThreadLocal使用

下面我们定义一个共享变量ThreadLocal,在main方法中创建两个线程t1与t2,分别在run方法中先调用threadlocal的get方法 ,然后调用threadlocal的set方法,将当前的线程名称set进去,最后在调用get方法。


public class ThreadLocalTest01 {

    public static ThreadLocal<String>  threadLocal= new ThreadLocal<>();
    public static void main(String[] args) throws InterruptedException {
        Thread t1= new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1 get:"+threadLocal.get());
                // 将当前线程名称set进去
                threadLocal.set(Thread.currentThread().getName());

                System.out.println("t1 get:"+threadLocal.get());
            }
        },"t1");
        Thread t2= new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t2 get: "+threadLocal.get());
				// 将当前线程名称set进去
                threadLocal.set(Thread.currentThread().getName());
                System.out.println("t2 get: "+threadLocal.get());
            }
        },"t2");

        t1.start();
        t2.start();
        Thread.sleep(2000);
    }
}

运行结果:

t1 get:null
t2 get: null
t1 get:t1
t2 get: t2

正如结果所见,在没有set值之前,get不到值,当前线程只能get得到当前线程set的值,线程之间互不影响,比如说t1 线程set(“t1”),在get的时候只能得到t1。

源码分析

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

从该段代码中可以看出,首先获得当前线程对象,然后又通过调用getMap方法并把当前线程作为参数传入, 获得了一个ThreadLocalMap的一个对象map,最后判断map是否为null, 不是null就调用map的set方法,是null就调用createMap方法,字面意思就是创建一个map。
看到这,现在有几个疑惑1⃣️ getMap方法干了啥 2⃣️ThreadLocalMap 这个类又是啥 3⃣️ createMap 又是怎么干的。
为了更好的理解,我们先看看ThreadLocalMap 是啥?

ThreadLocalMap

其实ThreadLocalMap 是在ThreadLocal中的一个静态内部类。它的真实面目就是key,value类型的数据结构,可以理解为跟HashMap差不多,下面是它的一部分代码。

static class ThreadLocalMap {

		private static final int INITIAL_CAPACITY = 16;
		private Entry[] table;
		private int size = 0;
		static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
       	....
        ....
}
getMap(Thread t)

接下来我们看看着个getMap干了啥?

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

获取当前线程对象的一个成员threadLocals。原来在Thread类中维护着一个ThreadLocalMap 类型的成员。
定义如下:

public   class Thread implements Runnable {
		ThreadLocal.ThreadLocalMap threadLocals = null;
}
createMap(t, value)

当我们获取的map==null的时候 ,说明之前没有初始化过这个成员,这时候就需要我们createMap出场来初始化线程对象的threadlocals成员了。

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

new 一个ThreadLocalMap对象赋值给当前线程的threadLocals成员,并将当前的threadlocal对象作为key,value设置进去。
到这里这个设计模型已经很清楚了
就是每个Thread对象中维护着一个map,每当我们往ThreadLocal对象set值的时候,其实就是向当前线程的map中put 值。key 是ThreadLocal对象,value 就是你要设置的值
图解:
在这里插入图片描述

get()

接下来我们再说说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();
    }

首先也是获取当前线程对象,获取当前线程的成员threadLocals为map,然后判断map!=null的时候,通过this(也就是当前threadlocal 对象)获取到map的entry对象,entry对象不是null 就return entry.value。 如果获取的map是null的话 就return setInitialValue方法。
前半段很简单,map不是null就跟HashMap取值差不多原理。
接下来看看setInitialValue 方法干了啥?

setInitialValue()

这段代码 除了第一行 跟set方法一样。这样我们只需要看看这个第一行的initialValue()方法干了啥就行了。

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

initialValue 方法
从字面意思上看是初始化value。这里return了个null,这里其实是留给继承ThreadLocal的子类来实现的。

 protected T initialValue() {
        return null;
  }

总结

以上就是我们ThreadLocal常用的set,get 原理解析,ThreadLocal操作的就是当前线程中一个threadlocals成员变量。就是当前线程中一个map的put,get操作。 其实ThreadLocal的不具有传承性,也就是子线程不具有父线程的键值信息。这个就需要InheritableThreadLocal 类来实现了,这个类的原理解析可以看《InheritableThreadLocal原理解析》

猜你喜欢

转载自blog.csdn.net/yuanshangshenghuo/article/details/96310427