1. 程式人生 > 其它 >Windows Server 2012 R2 遠端桌面服務部署指南

Windows Server 2012 R2 遠端桌面服務部署指南

一句話描述

threadLocal,是執行緒本地變數,每個thread例項持有一個ThreadLocalMap型別的threadLocals屬性,map的key為ThreadLocal變數,value為呼叫threadLocal.set()的值,也就是真正有用的資料。

基礎

簡單例子

package com.xy.thread.threadlocal.ch01;

/**
 * <p>
 *  常用寫法:
 *  1.如果需要設定初始值,可以使用匿名類,或者定義子類繼承Threadlocal
 *  2.不需要初始值可以直接new ThreadLocal()建立物件
 * </p>
 *
 * @author liufuquan
 * @since 2022-02-15
 */
public class NumService implements Runnable{

    //匿名類語法,繼承類或者實現介面
    // initialValue方法為protected方法,繼承ThreadLocal類重寫initialValue方法設定初始值
    private ThreadLocal<Integer> numThreadLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };


    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            Integer integer = numThreadLocal.get();
            System.out.println(Thread.currentThread().getName() + ":" + integer);
            numThreadLocal.set(++integer);
        }
        //使用完之後,要清除
        numThreadLocal.remove();
    }
}
package com.xy.thread.threadlocal.ch01;

import java.util.Scanner;

/**
 * <p>
 *
 * </p>
 *
 * @author liufuquan
 * @since 2022-02-15
 */
public class NumServiceTest {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            new Thread(new NumService()).start();
        }
        Scanner input = new Scanner(System.in);
        input.nextLine();
    }
}

執行緒關聯的原理

簡介

/**
* This class provides thread-local variables. 
**/
public class ThreadLocal<T> {}

thread-local variables,官方的註釋,說明它的含義,執行緒本地變數。
ThreadLocal 並不是一個獨立的存在, 它與 Thread 類是存在耦合的, java.lang.Thread 類針對 ThreadLocal 提供瞭如下支援:

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class.
     與此執行緒有關的 ThreadLocal 值。此對映由 ThreadLocal 類維護*/
ThreadLocal.ThreadLocalMap threadLocals = null;

每個執行緒thread物件中有一個Thread.ThreadLocalMap欄位,使用時,呼叫ThreadLocal物件的set時,放到threadLocalMap中,key為ThreadLocal,value為設定的值。(每個執行緒都將自己維護一個 ThreadLocal.ThreadLocalMap 類在上下文中; map的key是ThreadLocal物件,value是與執行緒繫結的,真正用的用到的值。 )

uml關係

程式碼解析

ThreadLocal.set()方法,set 方法其實是將 target value 放到當前執行緒的 ThreadLocalMap 中, 而 ThreadLocal 類自己僅僅作為該 target value 所對應的 key

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

get 方法也是類似的道理, 從執行緒的 ThreadLocalMap 中獲取以當前 ThreadLocal 為 key 對應的 value

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

如果沒有 set 過 value, 此處 get() 將返回 null或者initialValue()設定的預設值。不過 initialValue() 方法是一個 protected 方法, 需要重寫邏輯實現自定義的初始預設值。

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

protected T initialValue() {
    return null;
}

使用場景

Spring中@Transactional註解,使用ThreadLocal儲存資料庫連線connection,保證事務方法每次拿到的都是同一個connection
Spring中Bean在singleton作用域時,使用ThreadLocal解決共享變數的執行緒安全問題

進階

ThreadLocalMap資料結構

ThreadLocalMap是雜湊表,但是與HashMap不同的是,出現hash衝突後的解決辦法:hashmap使用拉鍊法,ThreadLocalMap使用線性探索法,發生衝突就在陣列中向後尋找空的位置插入。

InheritableThreadLocal

一句話描述

InheritableThreadLocal,可繼承的執行緒本地變數,每個執行緒例項持有ThreadLocalMap型別的inheritableThreadLocals,父執行緒建立子執行緒時會將父執行緒的inheritableThreadLocals複製到子執行緒的inheritableThreadLocals,從而實現執行緒本地變數可以傳遞到子執行緒。
InheritableThreadLocals類通過重寫getMap和createMap兩個方法將本地變數儲存到了具體執行緒的inheritableThreadLocals變數中,當執行緒通過InheritableThreadLocals例項的set或者get方法設定變數的時候,就會建立當前執行緒的inheritableThreadLocals變數。而父執行緒建立子執行緒的時候,ThreadLocalMap中的建構函式會將父執行緒的inheritableThreadLocals中的變數「複製一份到子執行緒的inheritableThreadLocals」變數中。

class Thread implements Runnable{
     /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

執行緒的init方法

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    
    //(1)獲取當前執行緒(父執行緒)
    Thread parent = currentThread();
    //(2)父執行緒的inheritableThreadLocal和inheritThreadLocals=true
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    //(3)父執行緒的inheritableThreadLocals賦值給子執行緒的inheritableThreadLocals
    this.inheritableThreadLocals = 
         ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}

參考資料

https://mp.weixin.qq.com/s/x_dpL66o5S7x_PjncWLvUw
https://mp.weixin.qq.com/s/KbB_3JdR56Mw8O2gJEP2OA