线程安全问题的本质:对“共享资源”,“并发操作”导致的数据不一致问题。注意两个关键字:“共享资源”,“并发操作”
通常解决线程安全问题通过加锁,互斥访问共享资源
而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,没有互斥访问,在一定程度上可以提升程序的性能