<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • ThreadLocal的簡單理解

    一、背景

    最近有人問我ThreadLocal是如何做到在每個線程中的值都是隔離的,此處寫篇文章來簡單記錄下。

    二、ThreadLocal解決的問題

    1. 該數據屬于該線程Thread自身,別的線程無法對其影響。(需要注意:需要調用ThreadLocal的remove方法)
    2. 不存在線程安全問題。(因為ThreadLocal類型的變量只有自身的線程可以訪問,所以這點是成立的。)

    比如:

    用戶登錄成功后,需要將登錄用戶信息保存起來,以方便在系統中的任何地方都可以使用到,那么此時就可以使用ThreadLocal來實現。例如:Spring Security中的ThreadLocalSecurityContextHolderStrategy類。

    三、如何創建一個ThreadLocal實例

    private static final ThreadLocal<String> USER_NAME = new ThreadLocal<>();
    

    ThreadLocal的實例推薦使用private static final來修飾。

    四、ThreadLocal如何做到線程變量隔離

    1、理解3個類

    1. ThreadLocal: 此類提供了一個簡單的set,get,remove方法,用于設置,獲取或移除 綁定到線程本地變量中的值。

    2. ThreadLocalMap: 這是在ThreadLocal中定義的一個類,可以簡單的將它理解成一個Map,不過它的key是WeakReference弱引用類型,這樣當這個值沒有在別的地方引用時,在發生垃圾回收時,這個map的key會被自動回收,不過它的值不會被自動回收。

      static class Entry extends WeakReference<ThreadLocal<?>> {
      	Object value;
      	Entry(ThreadLocal<?> k, Object v) {
      		// key 弱引用
      		super(k);
      		// 值強引用
      		value = v;
      	}
      }
      
    3. Thread:這個是線程類,在這個類中存在一個threadLocals變量,具體的類型是ThreadLocal.ThreadLocalMap

    2、看下set方法是如何實現的

    public void set(T value) {
    	// 獲取當前線程
    	Thread t = Thread.currentThread();
    	// 獲取綁定到這個線程自身的 ThreadLocalMap,這個ThreadLocalMap是從Thread類的`threadLocals`變量中獲取的
    	ThreadLocalMap map = getMap(t);
    	if (map != null) {
    		// 向map中設置值,key為 ThreadLocal 對象的實例。
    		map.set(this, value);
    	} else {
    		// 如果map不存在,則創建出來。
    		createMap(t, value);
    	}
    }
    

    通過上方的代碼,我們可知: 當我們向ThreadLocal中設置一個值,會經過如下幾個步驟:

    1. 獲取當前線程Thread
    2. 獲取當前線程的 ThreadLocalMap 對象。
    3. ThreadLocalMap中設置值,key為ThreadLocal對象,值為具體的值。

    3、看看 get 方法如何實現

    public T get() {
    	// 獲取當前線程
    	Thread t = Thread.currentThread();
    	// 獲取這個線程自身綁定的 ThreadLocalMap 對象
    	ThreadLocalMap map = getMap(t);
    	if (map != null) {
    		// this是ThreadLocal對象,獲取Map中的Entry對象
    		ThreadLocalMap.Entry e = map.getEntry(this);
    		if (e != null) {
    			@SuppressWarnings("unchecked")
    			// 獲取具體的值
    			T result = (T)e.value;
    			return result;
    		}
    	}
    	// 設置初始值
    	return setInitialValue();
    }
    

    從上方的get 和 set 方法可以得知,通過往ThreadLocal對象中設置值或獲取值,其實是最終操作到Thread對象中的threadLocals字段中,而這個字段是Thread自身的,因此做到了隔離。

    五、ThreadLocalMap中的hash沖突是如何處理的

    1、ThreadLocal對象的hash值是怎樣的

    private final int threadLocalHashCode = nextHashCode();
    	// 該 ThreadLocal 對象自身的hash code值
        private final int threadLocalHashCode = nextHashCode();
    	// 從0開始
        private static AtomicInteger nextHashCode = new AtomicInteger();
    	// 每次遞增固定的值
        private static final int HASH_INCREMENT = 0x61c88647;
    	// hash code 值計算
        private static int nextHashCode() {
            return nextHashCode.getAndAdd(HASH_INCREMENT);
        }
    

    從上方的代碼中可以,ThreadLocal類在實例化出來之后,它的hash code值(threadLocalHashCode)就是固定的,即使ThreadLocal調用了set方法,設置了別的值,它的hash code值也不會發生變化。

    此字段threadLocalHashCodeThreadLocal對象的hash值,在ThreadLocalMap中需要用到這個hash值。

    2、解決hash沖突

    ThreadLocalMap hash沖突

    ThreadLocalMap解決hash沖突的辦法很簡單。就是通過線性探測法。如果發生了沖突,就去找數組后面的可用位置。具體看上圖。演示的是A和B 2個ThreadLocal對象,然后發生了沖突,A和B存在的位置在那個地方。

    六、ThreadLocal內存泄漏

    ThreadLocal為什么會存在內存泄漏呢?

    這是因為ThreadLocalMap中的keyWeakReference類型,也就是弱引用類型,而弱引用類型的數據在沒有外部強引用類型的話,在發生gc的時候,會自動被回收掉。注意: 此時是key被回收了,但是value是沒有回收的。因此在ThreadLocalMap中的Entry[]中可能存在keynull,但是value是具體的值的對象,因此就發生了內存泄漏。

    解決內存泄漏:

    當我們使用完ThreadLocal對象后,需要在適當的時機調用ThreadLocal#remove()方法。 否則就只有等Thread自動退出才能清除,如果是使用了線程池,Thread會重用,清除的機會就更難。

    posted @ 2022-06-28 10:36  huan1993  閱讀(206)  評論(0編輯  收藏  舉報
    国产美女a做受大片观看