Thread模式实现原理
Thread.java
public class Thread implements Runnable{
//这里省略了许多其他的代码
ThreadLocal.ThreadLocalMap threadLocals = null;
}
ThreadLocal.java
public class ThreadLocal<T>{
//这里省略了许多代码
//将value的值保存于当前线程的本地变量中
public void set(T value){
//获取当前线程
Thread t = Thread.currentThread();
//调用getMap方法获得当前线程中的本地变量ThreadLocalMap
ThreadLocalMap map = getMap(t);
//如果ThreadLocalMap已存在,直接使用
if(map != null)
//以当前的ThreadLocal的实例作为key,存储于当前线程的ThreadLocalMap中,
//如果当前线程中定义了多个不同的ThreadLocal的实例,则它们会作为不同key进行存储而不会相互干扰
map.set(this, value);
else
//如果ThreadLocalMap不存在,则为当前线程创建一个新的
create(t, value);
}
//获取当前线程中以当前ThreadLocal实例作为key的变量值
public T get(){
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if(map != null){
//获取当前线程中以当前ThreadLocal实例为key的变量值
ThreadLocalMap.Entry e = map.getEntry(this);
if(e != null)
return (T)e.value;
}
//当map不存在时,设置初始值
return setInitialValue();
}
//从当前线程中获取与之对应的ThreadLocalMap
ThreadLocalMap getMap(Thread t){
return t.threadLocals;
}
//创建当前线程中的ThreadLocalMap
void createMap(Thread t, T firstValue){
//调用构造函数生成当前线程中的ThreadLocalMap
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//ThreadLocalMap的定义
static class ThreadLocalMap{
//这里省略了许多代码
}
}
通过阅读源码,我们可以得知:
ThreadLocalMap变量属于线程的内部属性,不同的线程拥有完全不同的ThreadLocalMap变量
线程中的ThreadLocalMap变量的值是在ThreadLocal对象进行set或者get操作时创建的
在创建ThreadLocalMap之前,会先检查当前线程中的ThreadLocalMap变量是否已经存在,如果不存在则创建一个;如果已经存在,则使用当前线程已创建的ThreadLocalMap
使用当前线程的ThreadLocalMap的关键在于使用当前的ThreadLocal的实例作为key进行存储
ThreadLocal模式至少从两个方面完成了数据访问隔离,即横向隔离和纵向隔离,有了这两种不同的隔离方式,ThreadLocal模式就能真正地做到线程安全:
纵向隔离——线程与线程之间的数据访问隔离。这一点由线程的数据结构保证。因为每个线程在进行对象访问时,访问的都是各个线程自己的ThreadLocalMap
横向隔离——同一线程中,不同的ThreadLocal实例操作的对象之间相互隔离。这一点由ThreadLocalMap在存储时采用当前ThreadLocal的实例作为key来保证
深入比较ThreadLocal模式与synchronized关键字
ThreadLocal是一个Java类,通过对当前线程中的局部变量的操作来解决不同线程的变量访问的冲突问题。所以,ThreadLocal提供了线程安全的共享对象机制,每个线程都拥有其副本。
java中的synchronized是一个保留字,它依赖jvm的锁机制来实现临界区的函数或者变量在访问中的原子性。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。此时,被用作“锁机制”的变量是多个线程共享的
同步机制(synchronized关键字)采用了“以时间换空间”的方式,提供一份变量,让不同的线程排队访问。而ThreadLocal采用了“以空间换时间”的方式,为每一个线程都提供一份变量的副本,从而实现同时访问而互不影响。
ThreadLocal应用
场景:在同一个线程的不同开发层次中共享数据
步骤:
建立一个类,并在其中封装一个静态的ThreadLocal变量,使其成为一个共享数据环境
在类中实现访问静态ThreadLocal变量的静态方法(设值和取值)
作用:
使用ThreadLocal模式,可以使数据在不同的编程层次得到有效共享
使用ThreadLocal模式,可以对执行逻辑与执行数据进行有效解耦。即通过线程安全的共享对象来进行数据共享来取代通过参数和返回值来进行消息传递
参考:整理归纳自《struts2技术内幕——深入解析struts2架构设计与实现原理》