与Synchonized的对照: ThreadLocal和Synchonized都用于解决多线程并发訪问。但是ThreadLocal与synchronized有本质的差异。synchronized是利用锁的机制,使变量或代码块在某一时该仅仅能被一个线程訪问。而ThreadLocal为每一个线程都提供了变量的副本,使得每一个线程在某一时间訪问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通讯时能够得到数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。数据库
用法数组
把要线程隔离的数据放进ThreadLocaltomcat
1static ThreadLocal<T> threadLocal = new ThreadLocal<T>() { 2 protected T initialValue() { 3 这里通常new一个对象返回 4 } 5}
线程获取相关数据的时候只要服务器
1threadLocal.get();
想修改、赋值只要多线程
1threadLocal.set(val)
使用场景架构
如上面说到的,ThreadLocal是用于线程间的数据隔离,ThreadLocal为每一个线程都提供了变量的副本。并发
原理分析this
一、get()方法线程
1public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) {//当map已存在 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) { 7 @SuppressWarnings("unchecked") 8 T result = (T)e.value; 9 return result; 10 } 11 } 12 return setInitialValue();//初始化值 13} 14 15ThreadLocalMap getMap(Thread t) { 16 return t.threadLocals; 17}
上面先取到当前线程,而后调用getMap方法获取对应的ThreadLocalMap,ThreadLocalMap是ThreadLocal的静态内部类,而后Thread类中有一个这样类型成员,因此getMap是直接返回Thread的成员
1ThreadLocal.ThreadLocalMap threadLocals = null; 来看下ThreadLocal的内部类ThreadLocalMap源码,留个大体印象对象
1static class ThreadLocalMap { 2 private static final int INITIAL_CAPACITY = 16;//初始数组大小 3 private Entry[] table;//每一个能够拥有多个ThreadLocal 4 private int size = 0; 5 private int threshold;//扩容阀值 6 static class Entry extends WeakReference<ThreadLocal<?>> { 7 Object value; 8 9 Entry(ThreadLocal<?> k, Object v) { 10 super(k); 11 value = v; 12 } 13 } 14 15 private Entry getEntry(ThreadLocal<?> key) { 16 int i = key.threadLocalHashCode & (table.length - 1); 17 Entry e = table[i]; 18 if (e != null && e.get() == key) 19 return e; 20 else 21 return getEntryAfterMiss(key, i, e); 22 } 23 24 private void set(ThreadLocal<?> key, Object value) { 25 Entry[] tab = table; 26 int len = tab.length; 27 int i = key.threadLocalHashCode & (len-1); 28 for (Entry e = tab[i]; 29 e != null; 30 e = tab[i = nextIndex(i, len)]) { 31 ThreadLocal<?> k = e.get(); 32 if (k == key) { 33 e.value = value; 34 return; 35 } 36 if (k == null) { 37 //循环利用key过时的Entry 38 replaceStaleEntry(key, value, i); 39 return; 40 } 41 } 42 tab[i] = new Entry(key, value); 43 int sz = ++size; 44 if (!cleanSomeSlots(i, sz) && sz >= threshold) 45 rehash(); 46 } 47 ... 48}
能够看到有个Entry内部静态类,它继承了WeakReference,总之它记录了两个信息,一个是ThreadLocal<?>类型,一个是Object类型的值。getEntry方法则是获取某个ThreadLocal对应的值,set方法就是更新或赋值相应的ThreadLocal对应的值。里面涉及到扩容策略、Entry哈希冲突、循环利用等等再也不深刻,留个大体印象就好。
回顾下get()方法中的代码
1if (map != null) { 2 ThreadLocalMap.Entry e = map.getEntry(this); 3 if (e != null) { 4 @SuppressWarnings("unchecked") 5 T result = (T)e.value; 6 return result; 7 } 8} 9return setInitialValue();
map为null或e为null就会走到setInitialValue,若是咱们是第一次get()方法,那map会是空的,因此接下来先看setInitialValue()方法
1private T setInitialValue() { 2 //调用咱们实现的方法获得须要线程隔离的值 3 T value = initialValue(); 4 Thread t = Thread.currentThread(); 5 //拿到相应线程的ThreadLocalMap成员变量 6 ThreadLocalMap map = getMap(t); 7 if (map != null) 8 map.set(this, value); 9 else 10 createMap(t, value); 11 return value; 12}
上面initialValue就是实例化ThreadLocal要实现的方法,这里又取了线程的ThreadLocalMap,不为空就把值set进去(键为TreadLocal自己,值就是initialValue返回的值);为空就建立一个map同时添加一个值进去,最后返回value。
map.set(this, value)这句代码在上面的ThreadLocalMap源码中能够看到大体流程,下面看看createMap()作了什么事
1void createMap(Thread t, T firstValue) { 2 t.threadLocals = new ThreadLocalMap(this, firstValue); 3} 4 5 6ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { 7 table = new Entry[INITIAL_CAPACITY]; 8 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 9 //建立一个Entry,加入数组 10 table[i] = new Entry(firstKey, firstValue); 11 size = 1; 12 setThreshold(INITIAL_CAPACITY); 13}
能够看到在new ThreadLocalMap以后,就会建立一个Entry加入到数组中,最后把ThreadLocalMap的引用赋值给Thread的threadLocals成员变量
在回顾下get()方法中的代码
1if (map != null) { 2 ThreadLocalMap.Entry e = map.getEntry(this); 3 if (e != null) { 4 @SuppressWarnings("unchecked") 5 T result = (T)e.value; 6 return result; 7 } 8} 9return setInitialValue();
如今map不会为空了,再次调用get方法就会调用map的getEntry方法(上面的ThreadLocalMap源码中能够看到大体流程),拿到相应的Entry,而后就能够拿到相应的值返回出去
二、set方法
分析完get()方法,那么set()方法就天然而然的明白了,就再也不赘述
1public 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}
总结
ThreadLocal的实现原理是,在每一个线程中维护一个Map,键是ThreadLocal类型,值是Object类型。当想获取ThreadLocal的值时,就从当前线程中拿出Map,而后在把ThreadLocal自己做为键从Map中拿出值返回。
提供线程内的局部变量。每一个线程都本身管理本身的局部变量,互不影响
内存泄漏问题。能够看到ThreadLocalMap中的Entry是继承WeakReference的,其中ThreadLocal是以弱引用形式存在Entry中,若是ThreadLocal在外部没有被强引用,那么垃圾回收的时候就会被回收掉,又由于Entry中的value是强引用,就会出现内存泄漏。虽然ThreadLocal源码中的会对这种状况进行了处理,但仍是建议不须要用TreadLocal的时候,手动调remove方法。
架构技术进阶资料
+q q-q u n:948 368 769(免费获取如下资料)
+q q-q u n:948 368 769(免费获取如下资料)