线程安全问题的本质:对“共享资源”,“并发操作”导致的数据不一致问题。注意两个关键字:“共享资源”,“并发操作”

通常解决线程安全问题通过加锁,互斥访问共享资源
而ThreadLocal换了个思路解决问题,不访问“共享资源”,每个线程都访问自己的资源
简单用一下TheadLocal
public class ThreadLocalTest {
    public static void simpleUseThreadLocal(){
        ThreadLocal<Integer> threadLocalObj = new ThreadLocal();// 1
        threadLocalObj.set(1);// 2
        System.out.println(Thread.currentThread().getName()+":"+threadLocalObj.get());
    }
    public static void main(String[] args) throws InterruptedException {
        simpleUseThreadLocal();
    }
}
//输出, main:1看看ThreadLocal相关源码
public class ThreadLocal<T> {
    public ThreadLocal() {
    }
    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;
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    static class ThreadLocalMap {
        
    }
}
public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ....
}本程序的执行时,main线程的内存示意图

其实ThreadLocal的原理很简单,执行ThreadLocal的set方法时,可以获取当前的执行线程(Thread.currentThread()),线程下放入一个Map,Map中放入你的value,key为ThreadLocal对象。
get方法同理:获取执行线程,从执行线程的Map中get出key为“ThreadLocal对象”的value
1、ThreadLocal#set()方法源码分析

2、ThreadLocal#get()方法源码分析

3、RocketMQ应用案例
在RocketMQ中,应用程序使用RocketMQ的客户端向Broker发送消息时,为了保证Broker中的每个MessageQueue的负载均衡,客户端应该均衡的从选择MessageQueue进行发送
多线程并发使用一个生产者(DefaultMQProducer)不断地发送消息时,如何均衡的从多个MessageQueue中选择一个进行发送呢?

 RocketMQ就是使用的ThreadLocal,每个线程维护一个index,获取一个MessageQueue信息后index=index+1,这样每个线程就可以依次选择MessageQueue进行发送消息了。
如果不使用ThreadLocal,如何依次选择MessageQueue呢?可能就需要生产者维护一个index,然后多个线程同步互斥访问这个index了
4、总结
通过案例我们知道,在多线程并发编程时,为了保证线程安全,我们除了可以同步访问共享资源,还有另外一个选项:是不是可以访问共享资源呢?

特定的场景下选择ThreadLocal,没有互斥访问,在一定程度上可以提升程序的性能
                










