概念

ThreadLocal 叫做本地线程变量,也就是说,ThreadLocal 中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量。

作用

  1. 可以实现在同一个线程数据共享,从而解决多线程数据安全问题
  2. 可以通过set方法给当前线程关联一个数据
  3. 数据是以Map的形式存储的,key就是当前线程。数据的获取可以通过get方法
  4. 每一个ThreadLocal对象,只能为当前线程关联一个数据,如果想要关联多个数据,就需要使用多个ThreadLocal对象实例
  5. 在ThreadLocal中保存数据,在线程销毁后会自动释放

演示

Cat类
package com.threadLocal;

/**
 * @author long
 * @date 2022/9/15
 */
public class Cat {
}
Service类
package com.threadLocal;

/**
 * @author long
 * @date 2022/9/15
 */
public class Service {
    public void show(){
        System.out.println("Service当前线程=" + Thread.currentThread().getName());
        System.out.println("Service获取的对象是" + ThreadLocalTest.threadLocal.get());
        Dao dao = new Dao();
        dao.show();
    }
}
Dao类
package com.threadLocal;

/**
 * @author long
 * @date 2022/9/15
 */
public class Dao {
    public void show(){
        System.out.println("Dao当前线程=" + Thread.currentThread().getName());
        System.out.println("Dao获取的对象是" + ThreadLocalTest.threadLocal.get());
    }
}
ThreadLocalTest类
package com.threadLocal;

/**
 * @author long
 * @date 2022/9/15
 */
public class ThreadLocalTest {
    public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    private static class MyTest implements Runnable{
        @Override
        public void run() {
            Cat cat = new Cat();
            threadLocal.set(cat);
            System.out.println("run方法设置对象为" + cat);
            Service service = new Service();
            service.show();
        }
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new MyTest());
        thread.start();
    }
}

源码

ThreadLocal里边最重要的是有两个个方法

set()

public void set(T value) {
    // 获取到当前线程
    Thread t = Thread.currentThread();
    // 根据当前线程来获取其对应的Map
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // 如果Map不为空,就将数据存放进去;这里的this是set的方法调用者(ThreadLocal对象),所以这里Map中的键值对是唯一的
        map.set(this, value);
    else
        // 如果Map不存在,就创建一个和当前线程对应的Map
        createMap(t, value);
}

get()

public T get() {
    // 获取到当前线程
    Thread t = Thread.currentThread();
    // 根据当前线程来获取其对应的Map
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 如果Map不为空,就根据对应的ThreadLocal对象来获取存储的值
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            // 如果Entry不为空,就获取到存储的值返回
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
  1. 一个ThreadLocal对象只能存储一个k-v。因为ThreadLocal对象调用set()的时候,是以该ThreadLocal对象作为k值存储的。
  2. 要想存储多个值,就得创建多个ThreadLocal对象。

底层图示