1、目錄
2、ThreadLocal是什麼?有什麼用?
/** * 回顧synchronized在多線程共享線程的問題 * @author qiuyongAaron */ public class ThreadLocalOne { volatile Person person=new Person(); public synchronized String setAndGet(String name){ //System.out.print(Thread.currentThread().getName()+":"); person.name=name; //模擬網絡延遲 try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return person.name; } public static void main(String[] args) { ThreadLocalOne threadLocal=new ThreadLocalOne(); new Thread(()->System.out.println(threadLocal.setAndGet("arron")),"t1").start(); new Thread(()->System.out.println(threadLocal.setAndGet("tony")),"t2").start(); } } class Person{ String name="tom"; public Person(String name) { this.name=name; } public Person(){} } 運行結果: 無synchronized: t1:tony t2:tony 有synchronized: t1:arron t2:tony
- 無synchronized的時候,由於非原子操做,顯然不是預想結果,可參考我關於synchronized的討論。
- 如今,咱們的需求是:每一個線程獨立的設置獲取person信息,不被線程打擾。
- 由於,person是共享數據,用同步互斥鎖synchronized,當一個線程訪問共享數據的時候,其餘線程堵塞,再也不多餘贅述。
/** * 談談ThreadLocal的做用 * @author qiuyongAaron */ public class ThreadLocalThree { ThreadLocal<Person> threadLocal=new ThreadLocal<Person>(); public String setAndGet(String name){ threadLocal.set(new Person(name)); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return threadLocal.get().name; } public static void main(String[] args) { ThreadLocalThree threadLocal=new ThreadLocalThree(); new Thread(()->System.out.println("t1:"+threadLocal.setAndGet("arron")),"t1").start(); new Thread(()->System.out.println("t2:"+threadLocal.setAndGet("tony")),"t2").start(); } } 運行結果: t1:arron t2:tony
- ThreadLocal被稱爲線程局部變量,說白了,他就是線程工做內存的一小塊內存,用於存儲數據。
- 那麼,ThreadLocal.set()、ThreadLocal.get()方法,就至關於把數據存儲於線程本地,取也是在本地內存讀取。就不會像synchronized須要頻繁的修改主內存的數據,再把數據複製到工做內存,也大大提升訪問效率。
- 回到最開始的舉例,也就等價於mabatis、hibernate爲何要使用threadlocal來存儲session?
- 做用一:由於線程間的數據交互是經過工做內存與主存的頻繁讀寫完成通訊,然而存儲於線程本地內存,提升訪問效率,避免線程阻塞形成cpu吞吐率降低。
- 做用二:在多線程中,每個線程都須要維護session,輕易完成對線程獨享資源的操做。
3、ThreadLocal源碼簡要總結?
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
- 一個線程對應一個ThreadLocalMap ,能夠存儲多個ThreadLocal對象。
- ThreadLocal對象做爲key、獨享數據做爲value。
- ThreadLocalMap可參考HashMap,在ThreadMap裏面存在Entry數組也就是一個Entry一個鍵值對。
public T get() { Thread t = Thread.currentThread(); 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(); }
- 一個線程對應一個ThreadLocalMap,get()就是當前線程獲取本身的ThreadLocalMap。
- 線程根據使用那一小塊的threadlocal,根據ThreadLocal對象做爲key,去獲取存儲於ThreadLocalMap中的值。
4、ThreadLocal爲何會致使內存泄漏?
- Key使用強引用:也就是上述說的狀況,引用ThreadLocal的對象被回收了,ThreadLocal的引用ThreadLocalMap的Key爲強引用並無被回收,若是不手動回收的話,ThreadLocal將不會回收那麼將致使內存泄漏。
- Key使用弱引用:引用的ThreadLocal的對象被回收了,ThreadLocal的引用ThreadLocalMap的Key爲弱引用,若是內存回收,那麼將ThreadLocalMap的Key將會被回收,ThreadLocal也將被回收。value在ThreadLocalMap調用get、set、remove的時候就會被清除。
- 比較兩種狀況,咱們能夠發現:因爲
ThreadLocalMap
的生命週期跟Thread
同樣長,若是都沒有手動刪除對應key
,都會致使內存泄漏,可是使用弱引用能夠多一層保障:弱引用ThreadLocal
不會內存泄漏,對應的value
在下一次ThreadLocalMap
調用set
,get
,remove
的時候會被清除。