中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ThreadLocal的實現原理是什么?

發布時間:2020-05-22 13:47:31 來源:億速云 閱讀:179 作者:鴿子 欄目:編程語言

使用場景

假設我們有一個數據庫連接管理類:

class ConnectionManager {
    private static Connection connect = null;
    private static String url = System.getProperty("URL");

    public static Connection openConnection() {
        if(connect == null){
            try {
                connect = DriverManager.getConnection(url);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connect;
    }

    public static void closeConnection() {
        if(connect!=null) {
            try {
                connect.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

如果這個類被用在多線程環境內,則會存在線程安全問題,那么可以對這兩個方法添加synchronized關鍵字進行同步處理,不過這樣會大大降低程序的性能,也可以將connection變成局部變量:

class ConnectionManager {
    private Connection connect = null;

    public Connection openConnection(String url) {
        if(connect == null){
            try {
                connect = DriverManager.getConnection(url);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connect;
    }

    public void closeConnection() {
        if(connect!=null) {
            try {
                connect.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

class ConnectionManagerTest {
    private String url = System.getProperty("URL");

    public void insert() {
        ConnectionManager connectionManager = new ConnectionManager();
        Connection connection = connectionManager.openConnection(this.url);
        //使用connection進行操作
        connectionManager.closeConnection();
    }
    public void update() {
        ConnectionManager connectionManager = new ConnectionManager();
        Connection connection = connectionManager.openConnection(this.url);
        //使用connection進行操作
        connectionManager.closeConnection();
    }
}

每個CURD方法都創建新的數據庫連接會造成數據庫的很大壓力,這里可以有兩種解決方案:

  1. 使用連接池管理連接,既不是每次都創建、銷毀連接,而是從一個連接池里借出可用的連接,用完將其歸還。

  2. 可以看到,這里connection的建立最好是這樣的:每個線程希望有自己獨立的連接來避免同步問題,在線程內部希望共用同一個連接來降低數據庫的壓力,那么使用ThreadLocal來管理數據庫連接就是最好的選擇了。它為每個線程維護了一個自己的連接,并且可以在線程內共享。
class ConnectionManager {
    private static String url = System.getProperty("URL");
    private static ThreadLocalconnectionHolder = ThreadLocal.withInitial(() -> {
        try {
            return DriverManager.getConnection(url);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    });

    public static Connection openConnection() {
        return connectionHolder.get();
    }

    public static void closeConnection() {
        Connection connect = connectionHolder.get();
        if(connect!=null) {
            try {
                connect.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

另外還可以用到其他需要每個線程管理一份自己的資源副本的地方:An Introduction to ThreadLocal in Java

實現原理

這里面涉及到三種對象的映射:Thread-ThreadLocal對象-ThreadLocal中存的具體內容,既然是每個線程都會有一個資源副本,那么這個從ThreadLocal對象到存儲內容的映射自然就會存在Thread對象里:

ThreadLocal.ThreadLocalMap threadLocals = null;

而ThreadLocal類只是提供了訪問這個Map的接口:

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();
}

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的內部類,實現了一個類似HashMap的功能,其內部維護了一個Entry數組,下標就是通過ThreadLocal對象的threadLocalHashCode計算得來。這個Entry繼承自WeakReference,實現對key,也就是ThreadLocal的弱引用:

static class Entry extends WeakReference<threadlocal
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}
</threadlocal

內存模型圖如下:

ThreadLocal的實現原理是什么?

當ThreadLocal Ref出棧后,由于ThreadLocalMap中Entry對ThreadLocal只是弱引用,所以ThreadLocal對象會被回收,Entry的key會變成null,然后在每次get/set/remove ThreadLocalMap中的值的時候,會自動清理key為null的value,這樣value也能被回收了。

注意:如果ThreadLocal Ref一直沒有出棧(例如上面的connectionHolder,通常我們需要保證ThreadLocal為單例且全局可訪問,所以設為static),具有跟Thread相同的生命周期,那么這里的虛引用便形同虛設了,所以使用完后記得調用ThreadLocal.remove將其對應的value清除。

另外,由于ThreadLocalMap中只對ThreadLocal是弱引用,對value是強引用,如果ThreadLocal因為沒有其他強引用而被回收,之后也沒有調用過get/set,那么就會產生內存泄露,

在使用線程池時,線程會被復用,那么里面保存的ThreadLocalMap同樣也會被復用,會造成線程之間的資源沒有被隔離,所以在線程歸還回線程池時要記得調用remove方法。

hash沖突

上面提到ThreadLocalMap是自己實現的類似HashMap的功能,當出現Hash沖突(通過兩個key對象的hash值計算得到同一個數組下標)時,它沒有采用鏈表模式,而是采用的線性探測的方法,既當發生沖突后,就線性查找數組中空閑的位置。

當數組較大時,這個性能會很差,所以建議盡量控制ThreadLocal的數量。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

抚顺市| 信丰县| 黑山县| 宁明县| 乌审旗| 名山县| 攀枝花市| 米易县| 高州市| 芜湖市| 运城市| 平果县| 吉安市| 会泽县| 新竹县| 鸡西市| 来凤县| 榕江县| 叶城县| 喀什市| 星座| 灵寿县| 巴东县| 田阳县| 绥宁县| 凌云县| 日照市| 乳山市| 武川县| 岢岚县| 阿合奇县| 台江县| 兖州市| 荣成市| 伊金霍洛旗| 平陆县| 黎平县| 繁峙县| 武宣县| 吴桥县| 利辛县|