JAVA并发编程(2):ThreadLocal的使用以及实现原理

1.使用

ThreadLocal是java中的线程本地变量,如果创建了一个ThreadLocal变量,那么多线程中,每个线程都会拥有一个该变量的副本,具体使用方法如下:

public class ThreadLocalTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadLocalTest.class);

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set("thread1 local");
                LOGGER.info(threadLocal.get());
                threadLocal.remove();
                LOGGER.info(threadLocal.get());
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set("thread2 local");
                LOGGER.info(threadLocal.get());
            }
        });

        thread1.start();
        thread2.start();
    }
}

输出:

19:24:20.720 [Thread-1] INFO com.example.demo.ThreadLocalTest - thread2 local
19:24:20.720 [Thread-0] INFO com.example.demo.ThreadLocalTest - thread1 local
19:24:20.723 [Thread-0] INFO com.example.demo.ThreadLocalTest - null

可以看到两个线程中的ThreadLocal变量互不干扰

2.原理

 

 其类图如上,可以看到在每个线程中都有一个用于存储线程本地变量的Map,接下里分析ThreadLocal最重要的三个方法,看看ThreadLocal如何与ThreadLocalMap交互:

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

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

 

set方法就是将值保存到当前调用该方法的线程ThreadLocalMap变量中,其中key为ThreadLocal引用,value为要保存的值,这样就可以解释为什么ThreadLocal变量能够在每个线程中互不干扰,因为保存在不同线程的ThreadLocalMap中。

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

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

protected T initialValue() {
return null;
}

get方法则是从调用该方法的当前线程中拿到ThreadLocalMap,并以ThreadLocal为key,获取对应的值,其中如果没有获取到对应的value,则返回默认值null

(3)remove方法

public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

remove方法也不如所料,从当前线程中的ThreadLocalMap中删除对应的ThreadLocal变量。

分析完源码,可以看到ThreadLocal本质上只是一个外壳类,不保存数据,数据都保存在Thread类中的ThreadLocalMap中,其中ThreadLocalMap并没有使用集合中的HashMap,而是实现了一个简单的开发地址法的Map。

posted @ 2021-12-23 20:04  取名好烦呀  阅读(129)  评论(0编辑  收藏  举报