参考资料:
http://www.cnblogs.com/dolphin0520/p/3920407.html
http://blog.csdn.net/lufeng20/article/details/24314381
http://blog.csdn.net/devilkin64/article/details/7916630
1. 关于ThreadLocal
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
创建线程局部私有的变量,避免多线程之间的竞争,是一种简单的多线程安全类。
我对ThreadLocal的理解就是:某个数据是共享的,但是共享的粒度是线程内部,即该线程里运行的所有代码,所有方法内获取到的都是同一个值。
区别与其他的形式:如果不用ThreadLocal,而是使用static 变量,创建出的全局共享的话,在多线程的环境下,需要同步(简单的例子是多线程下的双判定的单例模式),如数据库连接管理类ConnectionManager,这个例子见下:
class ConnectionManager {
private static Connection connect = null;
public synchronized static Connection openConnection() {
if(connect == null){
connect = DriverManager.getConnection();
}
return connect;
}
public synchronized static void closeConnection() {
if(connect!=null)
connect.close();
}
}
一个简单的同步是直接使用synchronized 方法,但是这种的实现使得每次只能有一个线程在操作ConnectionManager.但是这种情况下,并发度太小(就这个情景下,使用连接池性能更好,这里只是使用这个说明做个ThreadLocal引入的例子),变成每个线程一个connectionmanager,这样的话,就不用在connectionmanager类中使用同步方法了,增加了处理时间。
那ThreadLocal和直接的将某个对象变成普通的局部变量,有什么不同呢?
如下面的代码:
class ConnectionManager {
private Connection connect = null;
public Connection openConnection() {
if(connect == null){
connect = DriverManager.getConnection();
}
return connect;
}
public void closeConnection() {
if(connect!=null)
connect.close();
}
}
我对次的理解是这样的,加入有个管理类Manager,里面有个成员是普通的成员变量SomeType obj,这样,每个Manager实例都有一个obj对象,这和上面需求的每个Thread一份obj还是不同的。而要做到每个Thread一份obj,要么将obj对象放到Thread子类或相关的类中(如Runnable等),或是,每创建一个Thread,手动的绑定一个obj对象,不然,是没有办法做到每个Thread一份obj。但是这样的话,使得一些独立的功能和Thread耦合起来了,或是使得你需要提前的需要知道Thread个数,好让你手动的创建obj对象,独立的绑定上去。一般来说,我们更希望一些工具类,管理类能够独立出去,而不是和Thread,Runanble杂在一起,能够自动的感知有多少的Thread,就创建多少的拷贝,这就是使用ThreadLocal的好处!!
2. 使用例子
class Manager{
static ThreadLocal< String> s=new ThreadLocal<String>();;
}
public class Test implements Runnable{
public static void main(String args[])
{
Test r1=new Test();
Thread s1=new Thread(r1);
Thread s2=new Thread(r1);
s1.start();
s2.start();
}
@Override
public void run() {
if(Manager.s.get()==null)
Manager.s.set(new String(Thread.currentThread().getName()));
System.out.println(Manager.s.get());
//use it do something else
}
}
3. 方法分析
a.构造函数
public ThreadLocal() { }
b.get()
public T get()
{
Thread t = Thread.currentThread();
//获取与当前线程绑定的ThreadLocalMap对象
//getMap(t) -> return t.threadLocals;
ThreadLocalMap map = getMap(t);
if (map != null)
{
//在hashmap中查询以当前ThreadLocal为key对象的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//因为ThreadLocaMap是懒加载的,即存在一个有效的条目时,才会创建出
//与当前Thread相关的threadLocalMap对象,所以,在set调用前,调用了
//get,会得到null的threadLocalMap对象
return setInitialValue();
}
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;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Thread对象中成员变量:ThreadLocal.ThreadLocalMap threadLocals = null;
关于ThreadLocal.ThreadLocalMap类:简而言之,就是一个map,里面的每个条目Entry的key为WeakReference类型的对象,value为该变量的该线程中的实际值(即如果如果该线程中的ThreadLocal对象没有其他对象引用了的话,该Entry就会被回收)
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?> > {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//ThreadLocalMap是一个线性探测hash表,默认的初始容量为16,threshold=2/3,ThreadLocal为key,当添加entry时,使用ThreadLocal的hashcode来在线性表中查找合适的位置
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);
}
set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//threadlocalmap的set方法
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();
}
4. 总结
- ThreadLocal有一个final类型的int变量(int threadLocalHashCode = nextHashCode()),表示该TreadLocal变量的hash值,该hash值有一个全局的AtomicInteger来管理,即系统中ThreadLocal变量有同一个AtomicInteger变量自增而得,因为Thread数母随机,基本能保证同一个Thread中的ThreadLocal变量hashcode不断变化,使得能均匀的映射到ThreadLocalMap上。
- 每个线程Thread对象都有一个ThreadLocal.ThreadLocalMap对象,线程的ThreadLocal变量存放在该ThreadLocalMap中。
- ThreadLocal为懒加载的,即当使用到第一个ThreadLocal对象时,才会创建出线程的ThreadLocalMap对象,该map为线程探测线性map,初始的默认大小为16,threshold为2/3.
- ThreadLocalMap使用循环数组存放兼值对ThreadLocalMap.Entry为WeakSoftReference子类,即当系统中不存在对ThreadLocal对象时,该Entry就会被回收(由于没有创建该WeakSoftReference对象相关的 reference queue,所以,只保证在内存不充足的时候该Entry被回收)
- 在ThreadLocalMap中查找时,首先使用ThreadLocal对象(key)的hashcode定位可能数据索引,然后依次遍历,直到找到对应的Entry,或是遇到null的Entry