标签:
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作为这个值的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 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 的值。
可以来看一下创建一个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 values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
......
}
再看这句:
if (map != null)
map.set(this, value);
也就是将该ThreadLocal实例作为key,要保持的对象作为值,设置到当前线程的ThreadLocalMap 中
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 }
1 获取当前的thread实例 Thread t = Thread.currentThread();
2 获取当前thread实例持有的hashmap ThreadLocalMap map = getMap(t);
3 判断该hashmap是否为空 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);
小结:
ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。
ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存
单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
因为每个线程拥有一个副本 也就没有了安全性问题 (用空间内存来换取的)
ThreadLocal的使用比synchronized要简单得多。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
其他线程必须排队等待 (以时间来换取的)
ThreadLocal并不能替代synchronized,它们处理不同的问题域。
Synchronized用于线程间的数据共享,实现同步机制,比ThreadLocal更加复杂。 保证多并发时数据的安全性
而ThreadLocal则用于线程间的数据隔离
标签:
原文地址:http://www.cnblogs.com/wihainan/p/4774385.html