ThreadLocal的使用与源码分析

ThreadLocal的使用与源码分析

从名称看,ThreadLocal是Thread和Local的组合,也就是说每一个Thread都拥有自己独立的一个Local变量副本。

ThreadLocal的简单使用

package com.morris.jvm.threadlocal;

import java.util.concurrent.TimeUnit;

/**
 * 演示ThreadLocal的使用
 */
public class SimpleThreadLocalDemo {


    public static void main(String[] args) {

    ThreadLocal<Person> threadLocal = new ThreadLocal<>();

        new Thread(() -> {
            call(threadLocal, "morris");
        }, "t1").start();

        new Thread(() -> {
            call(threadLocal, "bob");
        }, "t2").start();


    }

    private static void call(ThreadLocal<Person> threadLocal, String name) {
        Person person = new Person();
        person.name = name;
        threadLocal.set(person);

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": " +  threadLocal.get());
        threadLocal.remove();
        System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
    }

    private static class Person {
        String name;

        @Override
        public String toString() {
            return "Person{name=" + name + "}";
        }
    }

}

运行结果如下:

t1: Person{name=morris}
t2: Person{name=bob}
t2: null
t1: null

从运行结果可以发现,每个线程都有一份自己的Person对象,互不影响,当调用remove()之后,数据就会被清空。

源码分析

这里涉及到三个类,Thread类、ThreadLocal类以及ThreadLocal的静态内部类ThreadLocalMap。在Thread中有一个threadLocals变量,类型为ThreadLocal.ThreadLocalMap。

ThreadLocalMap可以简单理解为一个HashMap,其中key为ThreadLocal,value为调用ThreadLocal.set(T v)传入的值。

在这里插入图片描述

set()

java.lang.ThreadLocal#set

    public void set(T value) {
        Thread t = Thread.currentThread(); // 获取当前线程
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value); // key为ThreadLocal
        else
            createMap(t, value); // ThreadLocalMap不存在就创建
    }

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

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

调用ThreadLocal.set()方法会创建一个以当前ThreadLocal为key,传入的参数为value的键值对放入到当前线程的ThreadLocalMap中。

get()

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t); // 从当前线程中取出ThreadLocalMap
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); // 根据key从map中取出value
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue(); // key不存在则设置一个初始值
    }

调用ThreadLocal.get()方法会到当前线程的ThreadLocalMap中查找一个key为ThreadLocal的键值对所对应的值。

remove()

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

remove()方法很简单,就是从当前线程的ThreadLocalMap中移除key为ThreadLocal的键值对。

初始值

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

在调用ThreadLocal.get()方法时,如果当前线程的ThreadLocalMap没有被创建,或者在这之前没有调用过set()方法,就会调用setInitialValue()方法返回一个初始值,默认为null,子类可以重写这个方法自定义初始值。

ThreadLocal中提供了一个工厂方法来更优雅的实现自定义初始值。

    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {

        private final Supplier<? extends T> supplier;

        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }

        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }

SuppliedThreadLocal的使用

package com.morris.jvm.threadlocal;

/**
 * 带自定义初始值的ThreadLocal的使用
 */
public class SuppliedThreadLocalDemo {

    public static void main(String[] args) {

        ThreadLocal<String> threadLocal = ThreadLocal.withInitial(()->"hello");

        new Thread(() -> {
            threadLocal.set("morris");
            System.out.println(Thread.currentThread().getName() + ": " +  threadLocal.get());
            threadLocal.remove();
            System.out.println(Thread.currentThread().getName() + ": " +  threadLocal.get());
        }, "t1").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ": " +  threadLocal.get());
        }, "t2").start();

    }
}

运行结果如下:

t1: morris
t1: hello
t2: hello

值得注意的是,当调用remove()之后,再调用get()返回的是初始值而不是null,这点从源码中也可以分析得出。

更多精彩内容关注本人公众号:架构师升级之路
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u022812849/article/details/107485369