学习Java
中常用的开源框架,Mybatis
、Hibernate
中设计到线程通过数据库连接对象Connection
,对其数据进行操作,都会使用ThreadLocal
类来保证Java
多线程程序访问和数据库数据的一致性问题。就想深入了解一下ThreadLocal
类是怎样确保线程安全的!详解如下:
说起ThreadLocal
之前我想带大家先去看看Thread
中的两个变量方便后面的理解。
Thread类
public
class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
由Thread
源码得知每个Thread
类都会有着两个变量threadLocals
,inheritableThreadLocals
并且都是ThreadLocalMap
类型的变量,而ThreadLocalMap
是一个定制化的HashMap
,在默认情况下,每个线程中的这两个变量都为null
。知道了这些我们再看看今天的主菜ThreadLocad
:
ThreadLocad
set
方法
public void set(T value) {
//1.获取当前线程
Thread t = Thread.currentThread();
//2.将当前线程作为key,去查找对应的线程变量,找到则设置
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//3.第一次调用就创建当前线程对应的HashMap
createMap(t, value);
}
代码1首先获取调用线程,然后使用当前线程作为参数调用getMap(t)
方法,
getMap(Thread t)
的代码如下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看到,getMap(t)
的作用是获取线程自己的变量threadLocals
,threadLocals
变量被绑定到了线程的成员变量上。
如果getMap(t)
的返回值不为空,则把value
值设置到threadLocals
中,也就是把当前变量值放入当前线程的内存变量threadLocals
中。threadLocals
是一个HahsMap
结构,其中key
就是当前ThreadLocad
的实例对象引用,value
是通过set
方法传递的值。
如果getMap(t)
的返回值为空,说明是第一次调用set
方法,这时创建当前线程的threadLocals
变量。下面看看createMap(t,value)
做什么。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
他创建当前线程的threadLocals
变量。
-get
方法
public T get() {
//1.获取当前线程
Thread t = Thread.currentThread();
//2.获取当前线程的threadLocals变量
ThreadLocalMap map = getMap(t);
//3.如果threadLocals不为null,则返回对应的本地变量的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//4.threadLocals为空则初始化当前线程的threadLocals成员变量
return setInitialValue();
}
代码1首先获取当前线程实例,如果当前线程的threadLocals
变量不为null
,则直接返回当前线程绑定的本地变量,否则执行代码4进行初始化。setInitialValue()
代码如下:
private T setInitialValue() {
//初始化为null
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//如果当前线程的threadLocals变量不为空
if (map != null)
map.set(this, value);
else
//如果当前线程的threadLocals变量为空
createMap(t, value);
return value;
}
如果当前线程的threadLocals
变量不为空,则设置当前线程的本地变量为null
,否则调用createMap
方法创建当前线程的threadLocals
变量。
总结:
我个人认为ThreadLocad
就是一个工具类,他通过set
方法把value
值放入调用线程的threadLocals
里面并存放起来,当调用get
方法时,再从当前线程的threadLocals
变量里面将其拿出来使用。
小案例
两个线程去操作一个变量
public class ThreadLocadDemo1 {
static Integer i = 10;
static ThreadLocal<Integer> threadLocal=new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
threadLocal.set(i);
add();
});
Thread t2=new Thread(()->{
threadLocal.set(i);
del();
});
t1.start();
t2.start();
Thread.sleep(2000);
System.out.println(i);
}
private static void del() {
Integer integer = threadLocal.get();
System.out.println(integer-5);
}
private static void add() {
Integer integer = threadLocal.get();
System.out.println(integer+20);
}
}
执行结果
5
30
10