ThreadLocal是什么
ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
通常我们在使用jdbc做数据库连接时是需要手动控制事物的,这个时候就要保证数据库连接connection在同一个线程中的唯一性,而connection又不是单例的这个时候我们就要借助于ThreadLocal
ThreadLocal的用法
首先创建一个ThreadLocal对象,在获取值的时候调用get方法,判断我们拿到的值是否为null如果为null就创建一个要获取的对象,并且调用ThreadLocal的set方法将值设置进去。当以后我们在通过ThreadLocal的get方法获取值的时候,同一个线程中就是唯一的了
这是一个获取数据库连接的代码
private static ThreadLocal<Connection> tol = new ThreadLocal<Connection>();
//获取Connection连接
public static Connection getConnection() throws Exception{
//获取连接
Connection conn = tol.get();
if(conn==null){
//如果为空就创建一个
//加载驱动
Class.forName(prop.getProperty("driver"));
//创建连接
conn = DriverManager.getConnection(prop.getProperty("url"),
prop.getProperty("username"), prop.getProperty("password"));
//设置进入ThreadLocal
tol.set(conn);
}
//返回Connection
return conn;
}
ThreadLocal的工作原理
首先ThreadLocal是放在线程的ThreadLocalMap这个类型的成员变量中的,这个map是将ThreadLocal作为键,任意类型作为值,下面就是ThreadLocalMap的源码
static class Entry extends WeakReference<ThreadLocal<?>> {
/** 与此ThreadLocal关联的值. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
每次调用ThreadLocal.get()方法时,会首先获取当前线程对应的ThreadLocalMap,然后以当前ThreadLocal做键获取值,当没有取到时会调用setInitialValue方法。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
setInitialValue方法内部首先调用initialValue方法返回一个null,然后在获取当前线程,获取当前线程的ThreadLocalMap,如果当前线程存在就把initialValue方法的返回值放到map中,如果不存在就为当前线程创建一个ThreadLocalMap并且把initialValue方法的返回值放到map中。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
所以我们可以通过重写initialValue方法来使我们获取不到值得时候,直接在initialValue方法中为ThreadLocal设置值,下面就一个通过重写initialValue来获取值的例子
public class TestInitialValue {
static ThreadLocal<Long> idThreadLocal = new ThreadLocal(){
@Override
protected Long initialValue() {
System.out.println("==============");
return Thread.currentThread().getId();
}
};
public static void main(String[] args) {
Long mainId = idThreadLocal.get();
System.out.println("主线程id" + mainId);
Long mainId1 = idThreadLocal.get();
System.out.println("主线程id" + mainId1);
}
}
==============
主线程id1
主线程id1
可以看到当一次获取的时候是无法获取到值得,所以调用了initialValue方法,由于重写了initialValue方法,所以第二次是可以直接拿到值的,就没有调用initialValue方法