public class UserHolder {
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
public static void saveUser(UserDTO user){
tl.set(user);
}
public static UserDTO getUser(){
return tl.get();
}
public static void removeUser(){
tl.remove();
}
}
// 获取当前的登录用户id
Long userId = UserHolder.getUser().getId();
扩展:
每个线程都有一个成员变量ThreadLocalMap,当线程访问ThreadLocal修饰的共享数据时,该线程就会在自己的ThreadLocalMap中存储一份共享数据的副本,key指向ThreadLocal这个弱引用,value保存的是共享数据的副本,因为每个线程都有一份共享数据的副本,以此就解决了线程安全问题
ThreadLocal的set方法:
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 将当前元素存入ThreadLocalMap
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);
}
ThreadLocal.ThreadLocalMap threadLocals = null;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
// 节点的构造方法
Entry(ThreadLocal<?> k, Object v) {
// key赋值
super(k);
// value赋值
value = v;
}
}
小结:
实现ThreadLocal的关键点:
可以先看如下代码:
ThreadLocal threadLocal = new ThreadLocal();
threadLocal.set(new Object());
threadLocal = null;
InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
// ThreadLocal threadLocal = new ThreadLocal<>();
// 向主线程中的threadLocal设值
threadLocal.set("世界上最好的编程语言");
// 子线程
Thread sonThread = new Thread(){
@Override
public void run() {
// super.run();
System.out.println(threadLocal.get() + " 是Java");
}
};
sonThread.start();
在Thread类中,有 ThreadLocal.ThreadLocalMap类型的成员变量threadLocals和inheritableThreadLocals:
ThreadLocal.ThreadLocalMap threadLocals = null;
// ...
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
在Thread的init()方法中,如果父线程的 inheritableThreadLocals 不为空,就把它赋给当前线程(子线程)的 inheritableThreadLocals
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
// ....
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
*
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
synchronized可以用来修饰实例方法、静态方法、代码块,以保证程序代码的原子性
synchronized void method(){
// ...
}
synchronized static void method() {
// ...
}
synchronized (Person.class) {
// ...
}
当我们使用synchronized时,JVM会自动进行lock和unlock操作
synchronized修饰代码块时,JVM采用monitorenter、monitorexit两个指令来实现同步,monitorenter指向同步代码块的开始位置(lock操作),第一个monitorexit指向同步代码块的结束位置(unlock操作),第二个monitorexit保证出现异常也能unlock
synchronized修饰代码块时,采用ACC_SYNCHRONIZED来标识该方法是一个synchronized修饰的同步方法
monitorenter、monitorexit和ACC_SYNCHRONIZED都是基于Monitor对象实现的
实例对象结构中有对象头,对象头中MarkWord指针会指向Monitor,Monitor是一种同步工具 / 同步机制,在Java虚拟机(Hotspot)中,Monitor由ObjectMonitor实现,又称为内部锁,或者Monitor锁
Monitor的工作原理:
ObjectMonitor() {
_header = NULL ;
_count = 0 ; // 记录 线程获取锁的次数
_waiters = 0 ,
_recursions = 0 ; / /锁 的 重 ⼊ 次 数
_object = NULL ;
_owner = NULL ; // 指向持 有ObjectMonitor对 象 的线程
_WaitSet = NULL ; // 处 于wait状 态 的线程,会被 加 ⼊ 到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // 处 于 等 待 锁block状 态 的线程,会被 加 ⼊ 到 该 列 表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
去医院就诊的过程就和Monitor比较类似:
未完待续…