以JDBC Connection为例,每个dao都需要一个Connection对象用于进行数据库操作。因为Dao在Spring中是单例的,所以属性Connection可能会被多个线程访问,但是Connection并不是线程安全的。使用ThreadLocal可以很好的解决问题,每个线程在使用Connection时,拷贝一份Connection对象,各自使用各自的,从而避免了共享变量引发的线程安全问题:
复制 public final class ConnectionUtil {
private ConnectionUtil() {}
private static final ThreadLocal<Connection> conn = new ThreadLocal<>();
public static Connection getConn() {
Connection con = conn.get();
if (con == null) {
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("url", "userName", "password");
conn.set(con);
} catch (ClassNotFoundException | SQLException e) {
// ...
}
}
return con;
}
}
复制 ThreadLocal<String> tl = new ThreadLocal<>();
tl.set("字符串"); // 初始化ThreadLocal
tl.get(); // 获取变量
tl.remove(); // 使用完毕,清除变量,否则有可能造成内存泄漏
复制 /*ThreadLocal*/
public void set(T value) {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 当前线程中的threadLocals,该对象是一个Map
if (map != null)
map.set(this, value); // 如果map不为空,那么就向map中添加一个key为threadLocal,value为变量值的元素
else
createMap(t, value); // 如果map不存在,则创建一个map对象
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
复制 public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); // 获取指定key对应的Entry对象
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result; // 取到了立即返回
}
}
return setInitialValue(); // 没取到初始化并返回null
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread()); // 从threadLocals中移除key为tl的元素
if (m != null)
m.remove(this);
}
所以,ThreadLocal变量本质上是将变量存储在当前线程Thread对象的thredLocals属性中的,此属性类型是ThreadLocal的内部类ThreadLocalMap,存放的变量key为threadLocal对象,set、get、remove本质上都是对这个map进行操作。
复制 static class ThreadLocalMap {
// map 存储的元素类型, 他继承自WeakReference,是一个弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// ThreadLocalMap实际上是一个Entry[]数组
private Entry[] table;
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}