在项目中不少地方会用到ThreadLocal对象,用来实现线程之间资源隔离。现在通过图的方式接解刨其原理。
public static void main(String[] arg) throws InterruptedException, IOException {
final ThreadLocal t = new ThreadLocal();
Thread thread_1 = new Thread(new Runnable(){
@Override
public void run() {
t.set("thread_1");
try {
Thread.sleep(50);
System.out.println("thread_1");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.get());
}
});
Thread thread_2 = new Thread(new Runnable(){
@Override
public void run() {
t.set("thread_2");
try {
Thread.sleep(50);
System.out.println("thread_2");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.get());
}
});
Thread thread_3 = new Thread(new Runnable(){
@Override
public void run() {
t.set("thread_3");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.get());
}
});
thread_1.start();
thread_2.start();
thread_3.start();
System.in.read();
Thread.currentThread().join();
整个过程的关键就是:
1、每个线程都有自己的ThreadLocalMap对象;(ThreadLocal 多线程下资源隔离的根本原因)
2、各个线程在调用同一个ThreadLocal对象的set(value)设置值的时候,是往各自的ThreadLocalMap对象数组中设置值
3、至于当前值放置在数组中的下标位置,则是通过ThreadLocal对象的threadLocalHashCode计算而来。即多线程环境下ThreadLocal对象的threadLocalHashCode是共享的。
4、而ThreadLocal对象的threadLocalHashCode是一个原子自增的变量,通过类方法初始化值。
也即,ThreadLocal a = new ThreadLocal();就会初始化threadLocalHashCode值,这个值不会再变。所以,同一个线程在同一个ThreadLocal对象中set()值,只能保存最后一次set的值。
5、为什么每个线程都有自己的ThreadLocalMap对象,且是一个数组呢?
答:根据以上的分析,多个线程操作一个ThreadLocal对象就能达到线程之间资源隔离。而采用数组是因为可能一个线程需要通过多个ThreadLocal对象达到多个资源隔离。每个不同的ThreadLocal对象的threadLocalHashCode都不一样,也就映射到ThreadLocalMap对象数组下的不同下标。
6、每个线程的ThreadLocalMap对象是通过偏移位置的方式解决hash碰撞
7、每个线程都有自己的ThreadLocalMap对象也有扩容机制,且是天然线程安全的