ThreadLocal

ThreadLocal是什么呢?

其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。

线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,

是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地拥有和改变自己的副本,而不会和其它线程的副本冲突。

 

从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;
在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,
从而为多线程环境常出现的并发访问问题提供了一种隔离机制。

 

案例

 1     private static final ThreadLocal threadSession = new ThreadLocal(); 
 2   public static Session getSession() throws InfrastructureException { 
 3     Session s = (Session) threadSession.get(); 
 4     try { 
 5       if (s == null) { 
 6         s = getSessionFactory().openSession(); 
 7         threadSession.set(s); 
 8       } 
 9      } catch (HibernateException ex) { 
10            throw new InfrastructureException(ex); 
11     } 
12      return s; 
13 }

可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,
如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,
实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap(下面会讲到),
而threadSession作为这个map的key,要取得这个session可以通过threadSession.get()来得到,
里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。
显然,其他线程中是取不到这个session的,他们也只能取到自己的ThreadLocalMap中的东西。要是session是多个线程共享使用的,那还不乱套了。


试想如果不用ThreadLocal怎么来实现呢?

可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。
或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,
但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例作为key,
这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。

 

原理:

首先看Thread里面的代码:

1 public class Thread implements Runnable {  
2     ......  
3   
4     /* ThreadLocal values pertaining to this thread. This map is maintained 
5      * by the ThreadLocal class. */  
6     ThreadLocal.ThreadLocalMap threadLocals = null;    
7     ......  
8 } 

这个ThreadLocalMap 类是ThreadLocal中定义的内部类,但是它的实例却用在Thread类中。

看ThreadLocal:

 1 下面来看看ThreadLocal的实现原理(jdk1.5源码) 
 2 public class ThreadLocal<T> {
 3 
 4     private final int threadLocalHashCode = nextHashCode();
 5     private static int nextHashCode = 0;
 6     private static final int HASH_INCREMENT = 0x61c88647;
 7 
 8     private static synchronized int nextHashCode() {
 9         int h = nextHashCode;
10         nextHashCode = h + HASH_INCREMENT;
11         return h;
12     }
13 
14     public ThreadLocal() {
15     }
16 
17     public T get() {
18         Thread t = Thread.currentThread();
19         ThreadLocalMap map = getMap(t);
20         if (map != null)
21             return (T)map.get(this);
22 
23         // Maps are constructed lazily.  if the map for this thread
24         // doesn't exist, create it, with this ThreadLocal and its
25         // initial value as its only entry.
26         T value = initialValue();
27         createMap(t, value);
28         return value;
29     }
30 
31 
32     public void set(T value) {
33         Thread t = Thread.currentThread();
34         ThreadLocalMap map = getMap(t);
35         if (map != null)
36             map.set(this, value);
37         else
38             createMap(t, value);
39     }
40 
41 
42     ThreadLocalMap getMap(Thread t) {
43         return t.threadLocals;
44     }
45 
46     void createMap(Thread t, T firstValue) {
47         t.threadLocals = new ThreadLocalMap(this, firstValue);
48     }
49 
50     .......
51 
52     static class ThreadLocalMap {
53 private Entry[] table;
54 static class Entry extends WeakReference<ThreadLocal> { 55 /** The value associated with this ThreadLocal. */ 56 Object value; 57 58 Entry(ThreadLocal k, Object v) { 59 super(k); 60 value = v; 61 } 62 } 63 64 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { 65 table = new Entry[INITIAL_CAPACITY]; 66 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 67 table[i] = new Entry(firstKey, firstValue); 68 size = 1; 69 setThreshold(INITIAL_CAPACITY); 70 } 71 // ... ... 72 } 73 74 }

可以看到ThreadLocal类中的变量只有这3个int型:

1 private final int threadLocalHashCode = nextHashCode(); 
2 private static int nextHashCode = 0; 
3 private static final int HASH_INCREMENT = 0x61c88647;

而作为ThreadLocal实例的变量只有 threadLocalHashCode 这一个,nextHashCode 和HASH_INCREMENT 是ThreadLocal类的静态变量,
实际上HASH_INCREMENT是一个常量,表示了连续分配的两个ThreadLocal实例的threadLocalHashCode值的增量,
而nextHashCode 的表示了即将分配的下一个ThreadLocal实例的threadLocalHashCode 的值。

threadLocalHashCode的作用可以类比HashMap,用来唯一区分ThreadLocal,每一个ThreadLocal的实例,这个值都是不相同的

根据这个值可以从thradlocalmap中的entry数组中存取对应的值。

 

可以来看一下创建一个ThreadLocal实例即new ThreadLocal()时做了哪些操作,
从上面看到构造函数ThreadLocal()里什么操作都没有,唯一的操作是这句:
private final int threadLocalHashCode = nextHashCode();

那么nextHashCode()做了什么呢:

1 private static synchronized int nextHashCode() { 
2   int h = nextHashCode; 
3   nextHashCode = h + HASH_INCREMENT; 
4   return h; 
5 } 

就是将ThreadLocal类的下一个hashCode值即nextHashCode的值赋给实例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT这个值。
因此ThreadLocal实例的变量只有这个threadLocalHashCode,而且是final的,用来区分不同的ThreadLocal实例,ThreadLocal类主要是作为工具类来使用,
那么ThreadLocal.set()进去的对象是放在哪儿的呢?

看一下上面的set()方法,两句合并一下成为
ThreadLocalMap map = Thread.currentThread().threadLocals;

这个ThreadLocalMap 类是ThreadLocal中定义的内部类,但是它的实例却用在Thread类中:
public class Thread implements Runnable {
......
ThreadLocal.ThreadLocalMap threadLocals = null;
......
}

再看这句:
if (map != null)
map.set(this, value);

也就是将该ThreadLocal实例作为key,要保持的对象作为值,设置到当前线程的ThreadLocalMap 中

ThreadLocal的threadLocakhashcode用来确定在map中的entry数组中的位置 从而方便快速的读和设置值

每当new一个ThreadLocal实例 他的threadLocalHashcode的值都是不同的,从而2个threadlocal不会产生哈希冲突。

 

ThreadLocal的方法:

1 get

1     public T get() {  
2         Thread t = Thread.currentThread();  
3         ThreadLocalMap map = getMap(t);  
4         if (map != null)  
5             return (T)map.get(this);    
6         T value = initialValue();  
7         createMap(t, value);  
8         return value;  
9     }
42     ThreadLocalMap getMap(Thread t) {
43         return t.threadLocals;
44     }

1 获取当前的thread实例 Thread t = Thread.currentThread();
2 获取当前thread实例持有的map ThreadLocalMap map = getMap(t);
3 判断该map是否为空 if (map != null)
    1 not null : 按照key(this)值获取value return (T)map.get(this);
    2 null :
      1 生成一个初始化的值 T value = initialValue();
      2 为当前线程创建一个map createMap(t, value);
         void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
     3 返回初始化值 return value;

可以看出该map以this(当前threadlocal对象)做key值   所以一个threadlocal实例只能对应一个值

每当new threadlocal实例后 与他相关的threadLocalHashCode的值就会发生改变 所以多个threadlocal的threadLocalHashCode的不同

所以通过产生多个threadlocal实例  来为线程保存不同类型的变量

一个Thread中才有一个map实例,用它来存放多个threadlocal和object的键值对
那么当前thread就会保有多个不同类型的变量值

当线程销毁时相应的东西也一起销毁了

 

 

2protected T initialValue()

返回此线程局部变量的当前线程的初始值。
最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。
如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。
在程序中一般都重写initialValue方法,以给定一个特定的初始值。

 

3 void remove()

移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。
如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。

 

4 set

1     public void set(T value) {  
2         Thread t = Thread.currentThread();  
3         ThreadLocalMap map = getMap(t);  
4         if (map != null)  
5             map.set(this, value);  
6         else  
7             createMap(t, value);  
8     } 

1 获取当前的thread实例 Thread t = Thread.currentThread();
2 获取当前thread实例持有的hashmap ThreadLocalMap map = getMap(t);
3 判断该hashmap是否为空 if (map != null)
    1 not null : 以this为key设置值 map.set(this, value);
    2 null :
       1 生成一个初始化的值 T value = initialValue();
       2 为当前线程创建一个map createMap(t, value);

 1  1        private void set(ThreadLocal key, Object value) {
 2  2 
 3  3             Entry[] tab = table;
 4  4             int len = tab.length;
 5  5             int i = key.threadLocalHashCode & (len-1);//根据threadLocalHashCode确定在数组中的位置
 6  6 
 7  7             for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) {
 8  8                 ThreadLocal k = e.get();//获取该位置的的key
 9  9 
10 10                 if (k == key) {//2个key相同 值替换
11 11                     e.value = value;
12 12                     return;
13 13                 }
14 14 
15 15                 if (k == null) {
16 16                     replaceStaleEntry(key, value, i);
17 17                     return;
18 18                 }
19 19             }
20 20 
21 21             tab[i] = new Entry(key, value);//该位置没有key表明还没有插入一个值 插入该key-value
22 22            int sz = ++size; 
23 23        if (!cleanSomeSlots(i, sz) && sz >= threshold) 
24 24         rehash(); 
25 25 }

 

 

 

小结:

ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。
ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存
单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
因为每个线程拥有一个副本 也就没有了安全性问题 (用空间内存来换取的)
ThreadLocal的使用比synchronized要简单得多。

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
其他线程必须排队等待 (以时间来换取的)

ThreadLocal并不能替代synchronized,它们处理不同的问题域。
Synchronized用于线程间的数据共享,实现同步机制,比ThreadLocal更加复杂。 保证多并发时数据的安全性
而ThreadLocal则用于线程间的数据隔离

 

posted @ 2015-08-31 22:49  yweihainan  阅读(207)  评论(0编辑  收藏  举报