ThreadLocal的正確使用與原理
ThreadLocal是執行緒Thread中屬性threadLocals即ThreadLocal.ThreadLocalMap的管理者,ThreadLocal用於給每個執行緒操作自己執行緒的本地變數,透過執行緒私有從而保證執行緒安全性。
拿get()方法來說,執行緒的本地變數是存放線上程例項的屬性ThreadLocalMap上的,ThreadLocalMap本質上就是一個HashMap,ThreadLocal只是一個管理者,當我們的執行緒需要拿到自己的本地變數時,我們直接呼叫ThreadLocal去get本地變數即可。
因為get()方法底層會先獲取到當前執行緒,然後透過當前執行緒拿到他的屬性值ThreadLocalMap,如果ThreadLocalMap為空,則會呼叫ThreadLocal的初始化方法拿到初始值返回,如果不為空,則會拿該ThreadLocal作為key去獲取該執行緒下的ThreadLocalMap裡對應的value值。
執行緒的屬性值ThreadLocalMap中使用的 key 為 ThreadLocal 的弱引用,而value是強引用。所以,如果ThreadLocal沒有被外部強引用的情況下,在垃圾回收的時候,key 會被清理掉,而value 不會被清理掉。這樣的話,ThreadLocalMap 中就會出現 key 為 null 的 Entry。假如我們不做任何措施的話,value 永遠無法被 GC 回收,這個時候就可能會產生記憶體洩露。
因此針對這種情況,我們有兩種原則:
ThreadLocal申明為private static final。JDK建議ThreadLocal定義為private static,這樣ThreadLocal的弱引用問題則不存在了。private與final 儘可能不讓他人修改變更引用。static 表示為類屬性,只有在程式結束才會被回收。ThreadLocal使用後務必呼叫remove方法。
最簡單有效的方法是使用後將其移除。
InheritableThreadLocal類是ThreadLocal類的子類。ThreadLocal中每個執行緒擁有它自己的值,與ThreadLocal不同的是,InheritableThreadLocal允許一個執行緒以及該執行緒建立的所有子執行緒都可以訪問它儲存的值。
ThreadLocal使用
public class ThreadLocalTest { //第一種初始化方式 /** * 宣告為static是讓ThreadLocal例項隨著程式的結束才結束,這樣才不會讓GC回收了 * 宣告為final是讓ThreadLocal例項引用不會被替換,這樣子也不會因為被替換導致被GC回收 * 這兩個宣告都是為了避免作為key的ThreadLocal物件沒有外部強引用而導致被GC回收,從而導致記憶體洩漏的問題,因為ThreadLocalMap中的ThreadLocal * 物件作為key是弱引用,會被GC回收。 */ private static final ThreadLocalthreadLocalStr = ThreadLocal.withInitial(() -> "fresh"); private static AtomicInteger intGen = new AtomicInteger(0); //第二種初始化方式 private static final ThreadLocalthreadLocalInt = new ThreadLocal() { @Override public Integer initialValue() { return intGen.incrementAndGet(); } }; public static void main(String[] args) throws InterruptedException { ArrayListthreads = new ArrayList<>(); for (int i = 0; i < 2; i++) { Thread t = new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " " + threadLocalInt.get()); System.out.println(Thread.currentThread().getName() + " " + threadLocalStr.get()); TimeUnit.SECONDS.sleep(5); threadLocalStr.set("bojack horseman" + threadLocalInt.get()); System.out.println(Thread.currentThread().getName() + " " + threadLocalInt.get()); System.out.println(Thread.currentThread().getName() + " " + threadLocalStr.get()); } catch (InterruptedException e) { e.printStackTrace(); } finally { threadLocalInt.remove(); threadLocalStr.remove(); } }); t.start(); threads.add(t); } TimeUnit.SECONDS.sleep(2); System.out.println(threads); System.out.println(threadLocalStr); System.out.println(threadLocalInt); } /** * Thread-0 1 * Thread-1 2 * Thread-0 fresh * Thread-1 fresh * [Thread[Thread-0,5,main], Thread[Thread-1,5,main]] * java.lang.ThreadLocal$SuppliedThreadLocal@1ef7fe8e * cn.vv.schedule.test.ThreadLocalTest$1@6f79caec * Thread-1 2 * Thread-1 bojack horseman2 * Thread-0 1 * Thread-0 bojack horseman1 */ }
InheritableThreadLocal使用
public class InheritableThreadLocalTest { //第一種初始化方式 private static final InheritableThreadLocalthreadLocalStr = new InheritableThreadLocal() { @Override public String initialValue() { return "fresh"; } }; private static AtomicInteger intGen = new AtomicInteger(0); //第二種初始化方式 private static final ThreadLocalthreadLocalInt = new ThreadLocal() { @Override public Integer initialValue() { return intGen.incrementAndGet(); } }; public static void main(String[] args) throws InterruptedException { //如果是InheritableThreadLocal,則父執行緒建立的所有子執行緒都會複製一份父執行緒的執行緒變數,而不是去初始化一份執行緒變數 threadLocalStr.set("main"); ArrayListthreads = new ArrayList<>(); for (int i = 0; i < 2; i++) { Thread t = new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " " + threadLocalInt.get()); System.out.println(Thread.currentThread().getName() + " " + threadLocalStr.get()); TimeUnit.SECONDS.sleep(5); //子執行緒可以自由地改變自己的本地變數 threadLocalStr.set("bojack horseman" + threadLocalInt.get()); System.out.println(Thread.currentThread().getName() + " " + threadLocalInt.get()); System.out.println(Thread.currentThread().getName() + " " + threadLocalStr.get()); } catch (InterruptedException e) { e.printStackTrace(); } finally { threadLocalInt.remove(); threadLocalStr.remove(); } }); t.start(); threads.add(t); } TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + " " + threadLocalStr.get()); } /** * Thread-0 2 * Thread-1 1 * Thread-0 main * Thread-1 main * main main * Thread-0 2 * Thread-0 bojack horseman2 * Thread-1 1 * Thread-1 bojack horseman1 */ }
原文來自 :
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2850768/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PHP Opcache 的正確使用PHPopcache
- ThreadLocal 原理和使用場景分析thread
- 上帝與集合的正確用法
- 正確地使用加密與認證技術加密
- 面試中的 ThreadLocal 原理和使用場景面試thread
- Threadlocal的使用以及實現原理總結thread
- TiDB 的正確使用姿勢TiDB
- Redis的正確使用姿勢Redis
- Android中Handler的正確使用Android
- 正確高效使用 GoogleGo
- ThreadLocal原理分析thread
- ThreadLocal 原理分析thread
- 正確理解memcached,才能更好的使用
- laravel 使用 es 的正確姿勢Laravel
- 使用列舉的正確姿勢
- Protobuf在Cmake中的正確使用
- 使用快取的正確姿勢快取
- ThreadLocal及InheritableThreadLocal的原理剖析thread
- ThreadLocal的使用thread
- 如何正確使用ping呢
- Postman 正確使用姿勢Postman
- 如何正確使用 Slim 框架框架
- 如何正確使用async/await?AI
- ThreadLocal原理深入解析thread
- ThreadLocal用法及原理thread
- 如何正確的使用代理ip資源
- Spring Boot使用AOP的正確姿勢Spring Boot
- npm run dev 的正確使用姿勢NPMdev
- 原始碼|使用FutureTask的正確姿勢原始碼
- 使用 react Context API 的正確姿勢ReactContextAPI
- 在vscode使用editorconfig的正確姿勢VSCode
- 虛幻私塾的正確使用姿勢
- Swift中使用Contains的正確姿勢SwiftAI
- Java中ThreadLocal的用法和原理Javathread
- 原始碼|ThreadLocal的實現原理原始碼thread
- 執行緒本地ThreadLocal的介紹與使用!執行緒thread
- Redis 分散式鎖的正確實現原理演化歷程與 Redission 實戰總結Redis分散式
- 如何正確管理HBase的連線,從原理到實戰