您的位置:首页 > 编程语言 > Java开发

ThreadLocal的一点理解

2017-09-18 12:16 369 查看
最近在看Spring,了解到spring中绝大部分的bean都可以声明为singleton的,是因为spring对bean中的一些非线程安全的状态采用ThreadLocal来处理,让他们变成了线程安全的bean。例如在TranscationSynchronizationManaer中:

public abstract class TranscationSynchronizationManager{
//用于保存每个事务线程对应的connection或者session等资源
private static final ThreadLocal resources = new ThreadLocal();
//用于保存每个事务线程对应事务的名称
private static final ThreadLocal currentTranscationName = new ThreadLocal();
//用于保存每个事务线程对应事务的read-only状态
private static final ThreadLocal currentTranscationReadOnly = new ThreadLocal();
.....
}


ThreadLocal 是如何存储变量的呢?首先在Thread类中有一个变量threadLocals,类型为ThreadLocal中的静态内部类ThreadLocalMap,该变量是存储的实体。

ThreadLocal.ThreadLocalMap threadLocals = null;


ThreadLocalMap是ThreadLocal中的静态内部类,构造函数声明如下:


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);
}


从上面的代码可以看到,ThreadLocalMap有点类似于HashMap的存储。键为TheadLocal类型的变量,值为所要存储的与该ThreadLocal类型的变量的绑定值。table是一个数组,存储的变量类型为Entry,但是Entry中并没有定义用于解决hash冲突的next指针,这里采用的是线性探测再散列的方法解决冲突。

然后,就可以在ThreadLocal中可以调用get()方法来获取指定的值。

//ThreadLocal中的方法
public T get() {
Thread t = Thread.currentThread();//获取到当前的线程
ThreadLocalMap map = getMap(t);//获取到当前线程对象中存储的ThreadLocalMap变量
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//键为当前的ThreadLocal对象
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//THreadLocalMap中的方法
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);//这里就是尝试寻找下一个位置的元素
}


如果要往ThreadLocalMap中插入值

public void set(T value) {//THreadLocal中的方法
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//获取当前线程对象中存储的ThreadLocalMap对象
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//ThreadLocalMap中的变量
private void set(ThreadLocal<?> key, Object value) {
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();
}


另外附线程安全的几个层次:

1. 使用无状态对象

2. 线程封闭

3. 采用同步技术

其中线程封闭又包括 ad-hoc线程封闭,栈封闭,使用ThreadLocal
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring 线程安全