<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # 簡介 早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal為解決多線程程序的并發問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優美的多線程程序。 當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。 從線程的角度看,目標變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。 所以,在Java中編寫線程局部變量的代碼相對來說要笨拙一些,因此造成線程局部變量沒有在Java開發者中得到很好的普及。 # ThreadLocal的接口方法 ThreadLocal類接口很簡單,只有4個方法,我們先來了解一下: * `void set(Object value)`設置當前線程的線程局部變量的值。 * `public Object get()`該方法返回當前線程所對應的線程局部變量。 * `public void remove()`將當前線程局部變量的值刪除,目的是為了減少內存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快內存回收的速度。 * `protected Object initialValue()`返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。 值得一提的是,在JDK5.0中,ThreadLocal已經支持泛型,該類的類名已經變為`ThreadLocal<T>`。API方法也相應進行了調整,新版本的API方法分別是`void set(T value)`、`T get()`以及`T initialValue()`。 ThreadLocal是如何做到為每一個線程維護變量的副本的呢?其實實現的思路很簡單:在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。我們自己就可以提供一個簡單的實現版本: ~~~ package com.test; public class TestNum { // ①通過匿名內部類覆蓋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) { TestNum sn = new TestNum(); // ③ 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 TestNum sn; public TestClient(TestNum 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() + "]"); } } } } ~~~ 通常我們通過匿名內部類的方式定義ThreadLocal的子類,提供初始的變量值,如例子中①處所示。TestClient線程產生一組序列號,在③處,我們生成3個TestClient,它們共享同一個TestNum實例。運行以上代碼,在控制臺上輸出以下的結果: ~~~ thread[Thread-0] --> sn[1] thread[Thread-1] --> sn[1] thread[Thread-2] --> sn[1] thread[Thread-1] --> sn[2] thread[Thread-0] --> sn[2] thread[Thread-1] --> sn[3] thread[Thread-2] --> sn[2] thread[Thread-0] --> sn[3] thread[Thread-2] --> sn[3] ~~~ 考察輸出的結果信息,我們發現每個線程所產生的序號雖然都共享同一個TestNum實例,但它們并沒有發生相互干擾的情況,而是各自產生獨立的序列號,這是因為我們通過ThreadLocal為每一個線程提供了單獨的副本。 # ThreadLocal內存泄漏問題 ThreadLocal 的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。但是如果濫用 ThreadLocal,就可能會導致內存泄漏。下面,我們將圍繞三個方面來分析 ThreadLocal 內存泄漏的問題 * ThreadLocal 實現原理 * ThreadLocal為什么會內存泄漏 * ThreadLocal 最佳實踐 **ThreadLocal 實現原理** ![](https://box.kancloud.cn/431d62131631adabb1d84f09cfcbc639_604x364.png) ThreadLocal ThreadLocal的實現是這樣的:每個Thread 維護一個 ThreadLocalMap 映射表,這個映射表的 key 是 ThreadLocal實例本身,value 是真正需要存儲的 Object。 也就是說 ThreadLocal 本身并不存儲值,它只是作為一個 key 來讓線程從 ThreadLocalMap 獲取 value。值得注意的是圖中的虛線,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用作為 Key 的,弱引用的對象在 GC 時會被回收。 ## ThreadLocal為什么會內存泄漏 ThreadLocalMap使用ThreadLocal的弱引用作為key,如果一個ThreadLocal沒有外部強引用來引用它,那么系統 GC 的時候,這個ThreadLocal勢必會被回收,這樣一來,ThreadLocalMap中就會出現key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當前線程再遲遲不結束的話,這些key為null的Entry的value就會一直存在一條強引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠無法回收,造成內存泄漏。 其實,ThreadLocalMap的設計中已經考慮到這種情況,也加上了一些防護措施:在ThreadLocal的get(),set(),remove()的時候都會清除線程ThreadLocalMap里所有key為null的value。 但是這些被動的預防措施并不能保證不會內存泄漏: 使用static的ThreadLocal,延長了ThreadLocal的生命周期,可能導致的內存泄漏(參考[ThreadLocal 內存泄露的實例分析](http://blog.xiaohansong.com/2016/08/09/ThreadLocal-leak-analyze/))。 分配使用了ThreadLocal又不再調用get(),set(),remove()方法,那么就會導致內存泄漏。 ## 為什么使用弱引用 從表面上看內存泄漏的根源在于使用了弱引用。網上的文章大多著重分析ThreadLocal使用了弱引用會導致內存泄漏,但是另一個問題也同樣值得思考:為什么使用弱引用而不是強引用? 我們先來看看官方文檔的說法: To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys. 為了應對非常大和長時間的用途,哈希表使用弱引用的 key。 下面我們分兩種情況討論: * key 使用強引用:引用的ThreadLocal的對象被回收了,但是ThreadLocalMap還持有ThreadLocal的強引用,如果沒有手動刪除,ThreadLocal不會被回收,導致Entry內存泄漏。 * key 使用弱引用:引用的ThreadLocal的對象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使沒有手動刪除,ThreadLocal也會被回收。value在下一次ThreadLocalMap調用set,get,remove的時候會被清除。 比較兩種情況,我們可以發現:由于ThreadLocalMap的生命周期跟Thread一樣長,如果都沒有手動刪除對應key,都會導致內存泄漏,但是使用弱引用可以多一層保障:弱引用ThreadLocal不會內存泄漏,對應的value在下一次ThreadLocalMap調用set,get,remove的時候會被清除。 因此,ThreadLocal內存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一樣長,如果沒有手動刪除對應key就會導致內存泄漏,而不是因為弱引用。 ## ThreadLocal 最佳實踐 綜合上面的分析,我們可以理解ThreadLocal內存泄漏的前因后果,那么怎么避免內存泄漏呢? * 每次使用完ThreadLocal,都調用它的remove()方法,清除數據。 在使用線程池的情況下,沒有及時清理ThreadLocal,不僅是內存泄漏的問題,更嚴重的是可能導致業務邏輯出現問題。所以,使用ThreadLocal就跟加鎖完要解鎖一樣,用完就清理
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看