十三、ThreadLocal

一、什么是ThreadLocal

ThreadLocal,提供线程局部变量。这些变量与正常的变量不同,因为每个线程在访问ThreadLocal实例的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户lD或事务ID)与线程关联起来。

二、为什么要用ThreadLocal

实现每一个线程都有自己专属的本地变量副本,主要解决了让每个线程绑定自己的值,通过使用get()和set()方法,获取默认值或将其值更改为当前线程所存的副本的值从而避免了线程安全问题。

三、Demo

public class ThreadLocalDemo {
    public static void main(String[] args) {
        House house = new House();
        new Thread(() -> {
            try {
                for (int i = 1; i <= 15; i++) {
                    house.saleHouse();
                }
                System.out.println(Thread.currentThread().getName() + "\t" + "-- - 卖出:" + house.threadLocal.get());
            } finally {
                house.threadLocal.remove();
            }
        }, "AA").start();

        new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    house.saleHouse();
                }
                System.out.println(Thread.currentThread().getName() + "\t" + "-- - 卖出:" + house.threadLocal.get());
            } finally {
                house.threadLocal.remove();
            }
        }, "BB").start();

        System.out.println(Thread.currentThread().getName() + "\t" + "-- - 卖出:" + house.threadLocal.get());
    }
}

class House {
    ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);

    public void saleHouse() {
        Integer value = threadLocal.get();
        value++;
        threadLocal.set(value);
    }
}

四、总结

1、使用完后必须remove()回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的ThreadLocal变是,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用try-finally块进行回收。

2、ThreadLocalMap从字面上就可以看出这是一个保存ThreadLocal对象的map(其实是以ThreadLocal为Key),不过是经过了两层包装的ThreadLocal对象:

 

JVM内部维护了一个线程版的Map<Thread,T>(通过ThreadLocal对象的set方法,结果把ThreadLocal对象自己当做key,放进了ThreadLoalMap中),每个线程要用到这个T的时候,用当前的线程去Map里面获取,通过这样让每个线程都拥有了自己独立的变量
人手一份,竞争条件被彻底消除,在并发模式下是绝对安全的变量

原文地址:https://www.cnblogs.com/shiblog/p/15753636.html