一.ThreadLocal简介
1.ThreadLocal是用来存储线程级别的变量的,他的目的是降低同一个变量在同一个线程里面来回切换造成的开销。他存储的变量是绝对的线程安全的,一般我们在实在的应用中用在回话session里面比较多:代码如下
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
}
if (s == null) {
}
s = getSessionFactory().openSession();
threadSession.set(s);
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
return s;
}
同一个线程里面只需要通过getSession()就可以获取到session
二.为什么是绝对线程安全的?
1.首先我们来看看java.lang.ThreadLocal里面的set方法,源码如下:
public void set(T value) {
//1.首先他会先获取当前的线程
Thread t = Thread.currentThread();
//2.接着就是获取Thread的一个ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//3.会把ThreadLoacl作为key,将value存放景区
map.set(this, value);
else
createMap(t, value);
}
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
设置值得过程不可能存在线程安全的问题,因为不可能存在别的线程,来获取这个线程ThreadLocalMap
2.接着我们来看看java.lang.ThreadLocal里面的get方法,源码如下:
public T get() {
//1.首先取到当前线程
Thread t = Thread.currentThread();
//取到当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// map.getEntry(this),获取到的是这个ThreadLoacl为key最新的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
//获取数组里面最大的坐标,就是要去获取最新的Entry
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
获取值得过程也是绝对的线程安全的,不可能有别的线程能获取到当前线程的ThreadLocalMap,通过源码发现,他们的值是存在在一个Entry e = table[i];的数组里里面。
三.如何回收ThreadLoacl的
1.通过源码
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
我们会发现Entry他是继承WeakReference<ThreadLocal<?>>,他是一个弱引用,熟悉JVM的知识,可以知道,弱引用他在每次GC他都会被回收。
四.如何会有value
通过源码:
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
//当他发现key->ThrealLocal为空的时候,他就会自动释放了value
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
当他发现key->ThrealLocal为空的时候,他就会自动释放了value