JDK裡有一個ThreadLocal這麼一個類,其實起這個名字不是很貼近,這個類相當於給線程設定上了一個局部變數。使得,不會因為多線程訪問同一個資源而產生多線程同步問題。
因為這個ThreadLocal類裡面放的是每個線程都擁有一個副本,線程之間彼此不會互相影響。
現在這裡筆者將會從源碼的實現角度給大家講述一下ThreadLocal實現的原理。
首先我們來看一下set()這個方法的實現。
所有的講解筆者都將以注釋的形式給出。
public void set(T value) { //拿到當前線程對象 Thread t = Thread.currentThread(); //拿到一個Map對象,好,那麼問題來了。這個是一個什麼樣的map對象呢?? //其實這個ThreadLocalMap對象是ThreadLocal類內部自己實現的一個類似於HashMap這樣一個類 ThreadLocalMap map = getMap(t);//如果map非空,則將當前的ThreadLocal對象和這個set()方法的參數put到這個Map裡面去,//如果沒有將其建立。 if (map != null) map.set(this, value); else createMap(t, value); }
我們可以看到ThreadLocal這個類裡面確實定義了一個ThreadLocalMap類。裡面的實現和HashMap的差不多,筆者就不點進去看了。
再一個我們點進去get()方法裡面去看以下。
public T get() {//拿到當前的線程對象 Thread t = Thread.currentThread();//拿到ThreadLocalMap對象,到了這裡我們進去getMap()方法裡面去看一下 ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
相比,看到了前面的解釋, 各位讀者也很想進去看一看getMap的實現。
ThreadLocalMap getMap(Thread t) { //從這裡可以看出 ThreadLocalMap對象是由當前的線程對象持有的,//但是維護的工作是由ThreadLocal這個類來完成的。 return t.threadLocals; }
//createMap方法在當前線程對象內部持有的那個ThreadLocalMap對象為空白,會調用之//從這裡我們也可以看出是ThreadLocal這個類在維護這個關係void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }寫到這裡我們可以下一個結論。
ThreadLocal底層是使用一個自己實現的Map來儲存使用者set()進來的值。 那個map的鍵,即為當前的ThreadLocal對象。
有的朋友可能會問了。現在ThreadLocal只有一個對象。那麼Map裡的值還不是會被其他線程所共用。
其實這個不是的,筆者剛開始也是這麼考慮的----------->這個Map裡儲存的應該是當前的Thread對象和值。而不應該是上面那種。
其實不是的。 因為那個ThreadLocal類自己實現的Map對象是每個線程對象內部自己持有一份。所以說,每個線程對象內的ThreadLocalMap對象是不一樣的。所以,裡面的資料是不會被其他線程所共用,都是自己用自己的。
這裡筆者寫了一個demo來證明每個線程持有的ThreadLocalMap對象是不同的。
package multiThread;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.concurrent.CyclicBarrier;class MyThread implements Runnable{ private CyclicBarrier cyclicBarrier; /** * 第一個線程所持有的ThreadLocalMap對象 */ static Object object1 = null; /** * 第二個線程所持有的ThreadLocalMap對象 */ static Object object2 = null; public MyThread(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { ThreadLocal threadLocal = new ThreadLocal(); Thread thread = Thread.currentThread(); Class<?> clazz = ThreadLocal.class; Method createMap = null; try { Method[] methods = clazz.getDeclaredMethods(); Method createMapMethod = null; /** * 通過反射拿到createMap方法的Method對象 */ for(Method method : methods) { if("createMap".equals( method.getName() )) { createMapMethod = method; } } System.out.println( createMapMethod ); if(createMapMethod != null) { createMapMethod.setAccessible( true ); createMapMethod.invoke( threadLocal, thread, "helloworld" ); } /** * 通過執行getMap()方法拿到那個LocalThreadMap對象 */ Method getMap = clazz.getDeclaredMethod( "getMap", Thread.class ); getMap.setAccessible( true ); System.out.println( getMap.invoke( threadLocal, thread ) ); if("thread1".equals( Thread.currentThread().getName() )) { object1 = getMap.invoke( threadLocal, thread ); } if("thread2".equals( Thread.currentThread().getName() )) { object2 = getMap.invoke( threadLocal, thread ); } /** * 等兩個線程都先拿到那個ThreadLocalMap對象才開始執行CyclicBarrier構造方法裡面的那個Runnable介面的匿名類裡的run方法, * 由最後一個完成的線程執行。 */ cyclicBarrier.await(); } catch(Exception e) { e.printStackTrace(); } }}public class ThreadLocalTest{ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException, NoSuchMethodException, InstantiationException, InvocationTargetException { CyclicBarrier cyclicBarrier = new CyclicBarrier( 2, new Runnable() { @Override public void run() { /** * 判斷兩個線程對象內所持有的ThreadLocalMap是否為同一對象 */ System.out.println(MyThread.object1 == MyThread.object2); } } ); MyThread myThread = new MyThread( cyclicBarrier ); Thread thread1 = new Thread( myThread, "thread1" ); Thread thread2 = new Thread( myThread, "thread2" ); thread1.start(); thread2.start(); }}