1.简述ThreadLocalhtml
ThreadLocal实例一般做为静态的私有的(private static)字段出如今一个类中,这个类用来关联一个线程。ThreadLocal是一个线程级别的局部变量,下面是线程局部变量(ThreadLocal variables)的关键点:数组
A、当使用ThreadLocal维护变量时,若多个线程访问ThreadLocal实例,ThreadLocal为每一个使用该变量的线程提供了一个独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其余线程所对应的副本。安全
B、从线程的角度看,目标变量就像是线程的本地变量,这也是类名中Local所要表达的意思。并发
2.细看ThreadLocalide
ThreadLocal<T>类很简单,只有四个方法:函数
(1)void set(T value),该方法用来设置当前线程中变量的副本高并发
(2)public T get(),该方法是用来获取ThreadLocal在当前线程中保存的变量副本ui
(3)public void remove(),该方法用来移除当前线程中变量的副本,目的是为了减小内存的占用,该方法是JDK 5.0新增的方法。须要指出的是,当线程结束之后,对应线程的局部变量将自动被垃圾回收,因此显式调用该方法清除线程的局部变量并非必须的操做,但它能够加快内存回收的速度。this
(4)protected T initialValue(),该方法是一个protected方法,通常是用来在使用时进行重写的,它是一个延迟加载方法,ThreadLocal中的缺省实现直接返回一个null。spa
3.ThreadLocal示例
简单的使用方法以下:
package com.test; public class ThreadMain { // ①经过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() { public Integer initialValue() { return 0; } }; // ②获取下一个序列值 public int getNextNum() { seqNum.set(seqNum.get() + 1); return seqNum.get(); } public static void main(String[] args) { ThreadMain sn = new ThreadMain(); // ③ 3个线程共享sn,各自产生序列号 TestClient t1 = new TestClient(sn); TestClient t2 = new TestClient(sn); TestClient t3 = new TestClient(sn); t1.start(); t2.start(); t3.start(); } private static class TestClient extends Thread { private ThreadMain sn; public TestClient(ThreadMain sn) { this.sn = sn; } public void run() { for (int i = 0; i < 3; i++) { // ④每一个线程打出3个序列值 System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn[" + sn.getNextNum() + "]"); } } } }
结果以下:
thread[Thread-0] --> sn[1] thread[Thread-2] --> sn[1] thread[Thread-1] --> sn[1] thread[Thread-2] --> sn[2] thread[Thread-0] --> sn[2] thread[Thread-2] --> sn[3] thread[Thread-1] --> sn[2] thread[Thread-1] --> sn[3] thread[Thread-0] --> sn[3]
另外一个案例
package com.csu.thread; class GlobalVarManager { private static ThreadLocal<String> globalVars = new ThreadLocal<String>(){ protected String initialValue() { return "hello"; } }; public static ThreadLocal<String> getglobalVars() { return globalVars; } } class ThreadRun implements Runnable { private ThreadLocal<String> t; private String str; ThreadRun(ThreadLocal<String> temp, String s) { this.t = temp; this.str = s; } @Override public void run() { System.out.println(Thread.currentThread()+"改变前:" + t.get()); t.set(str); System.out.println(Thread.currentThread()+"改变后:" + t.get()); } } public class ThreadLocalTry { public static void main(String[] args) { for (int i =1; i < 5; i++) { new Thread(new ThreadRun(GlobalVarManager.getglobalVars(), ""+i)).start(); } } }
结果以下:
Thread[Thread-0,5,main]改变前:hello Thread[Thread-1,5,main]改变前:hello Thread[Thread-0,5,main]改变后:1 Thread[Thread-2,5,main]改变前:hello Thread[Thread-1,5,main]改变后:2 Thread[Thread-2,5,main]改变后:3 Thread[Thread-3,5,main]改变前:hello Thread[Thread-3,5,main]改变后:4
上述案例也可按以下方式来实现:
package com.csu.test; class GlobalVarManager { private static ThreadLocal<String> globalVars = new ThreadLocal<String>(){ protected String initialValue() { return "hello"; } }; public static String getGlobalVars() { return globalVars.get(); } public static void setGlobalVars(String str) { globalVars.set(str); } } class ThreadRun implements Runnable { private String str = null; public ThreadRun(String temp) { str = temp; } @Override public void run() { System.out.println(Thread.currentThread()+"改变前:" + GlobalVarManager.getGlobalVars()); GlobalVarManager.setGlobalVars(str); System.out.println(Thread.currentThread()+"改变后:" + GlobalVarManager.getGlobalVars()); } } public class ThreadLocalTest { public static void main(String[] args) { for (int i = 1; i < 5; i++) { new Thread(new ThreadRun("" + i)).start(); } } }
结果以下:
Thread[Thread-3,5,main]改变前:hello Thread[Thread-2,5,main]改变前:hello Thread[Thread-1,5,main]改变前:hello Thread[Thread-0,5,main]改变前:hello Thread[Thread-1,5,main]改变后:2 Thread[Thread-2,5,main]改变后:3 Thread[Thread-3,5,main]改变后:4 Thread[Thread-0,5,main]改变后:1
4.ThreadLocal的实现机制
此部份内容暂没有深刻研究,欲了解更多内容请参考https://www.cnblogs.com/dennyzhangdd/p/7978455.html
(1)get()方法源码以下:
(2)set()方法源码以下:
(3)remove()方法源码以下:
(4)上述几个函数涉及到以下两个函数
从前述源码能够看出,ThreadLocal的get、set、remove方法都是操做当前线程,而从Thread的源码能够看出该类有一个ThreadLocal.ThreadLocalMap类型的变量threadLocals,该变量在初次调用ThreadLocal的set()方法时经过createMap()方法初始化
5.ThreadLocalMap
ThreadLocalMap的部分源码以下:
/** * ThreadLocalMap is a customized hash map suitable only for * maintaining thread local values. No operations are exported * outside of the ThreadLocal class. The class is package private to * allow declaration of fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues are not * used, stale entries are guaranteed to be removed only when * the table starts running out of space. */ static class ThreadLocalMap { /** * 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; } } /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table; /** * The number of entries in the table. */ private int size = 0; /** * The next size value at which to resize. */ private int threshold; // Default to 0 /** * Set the resize threshold to maintain at worst a 2/3 load factor. */ private void setThreshold(int len) { threshold = len * 2 / 3; } /** * Increment i modulo len. */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } /** * Decrement i modulo len. */ private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } /** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ 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); } /** * Construct a new map including all Inheritable ThreadLocals * from given parent map. Called only by createInheritedMap. * * @param parentMap the map associated with parent thread. */ private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len]; for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }
此处重点关注一下ThreadLocalMap中的几个成员变量及方法
(1)private Entry[] table;
table是一个Entry类型的数组,该变量在ThreadLocalMap的构造函数中初始化
Entry是ThreadLocalMap的一个内部类
(2)set()方法
/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be 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(); }
(3)getEntry()方法
/** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ 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); } /** * Version of getEntry method for use when key is not found in * its direct hash slot. * * @param key the thread local object * @param i the table index for key's hash code * @param e the entry at table[i] * @return the entry associated with key, or null if no such */ private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; }
(4)remove()方法
/** * Remove the entry for key. */ private void remove(ThreadLocal<?> key) { 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)]) { if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }
6.总结
ThreadLocal通常都是声明在静态变量中,若是不断地建立ThreadLocal而没有调用其remove方法,将致使内存泄露,特别是在高并发的Web容器当中。
ThreadLocal在处理线程的局部变量时比synchronized同步机制解决线程安全问题更简单,更方便,且程序拥有更高的并发性。