Thread-Specific Storage

January 13, 2022

為了管理名稱,程式語言會有名稱空間概念,例如,視語言的不同,變數可能會有不同的範疇,像是區塊變數、區域變數、類別變數、物件值域、模組變數、全域變數等。

如果想以執行緒作為名稱空間呢?

執行緒專用空間

你可以使用 Map 之類的結構,以執行緒為鍵,儲存對應的物件,設計一個簡單的 ThreadLocal 類別:

class ThreadLocal<T> {
    private Map<Thread, T> storage = Collections.synchronizedMap(new WeakHashMap<>());

    T get() {
        return storage.get(Thread.currentThread());
    }

    void set(T value) {
        storage.put(Thread.currentThread(), value);
    }
    
    void remove() {
        storage.remove(Thread.currentThread());
    }
}

Thread 生命週期結束,被 JVM 回收之後,對應的資源也應當被移除,因此這邊使用了 WeakHashMap,作為 Thread 的鍵被回收後,對應的鍵/值也會從 WeakHashMap 移除。

ThreadLocal 實例通常作為 static 成員存在,例如,設計一個 ThreadScope

class ThreadScope {
    private static final ThreadLocal<Map<String, Object>> scopes = new ThreadLocal<>();

    static Map<String, Object> get() {
        var scope = scopes.get();

        if(scope == null) {
            scope = new HashMap<>();
            scopes.set(scope);
        }

        return scope;
    }
} 

這麼一來,想以執行緒作為名稱空間,設置新的變數與值,就可以如下:

ThreadScope.get().put("connectionId", 1);

想取得某執行緒名稱空間中的變數就可以如下:

ThreadScope.get().get("connectionId");

執行緒/資源

Thread-Specific Storage 就是以執行緒為單位,儲存對應的資源,方才只是以名稱空間為例,實際上是為每個執行緒儲存對應的 HashMap

這類應用也可以用來做資源快取,例如:

public class Resource {
    private static final ThreadLocal<Resource> resources = new ThreadLocal<>();

    public static Resource getResource() {
        var resource = resources.get();

        if(resource == null) {
            resource = new Resource();
            resources.set(resource);
        }

        return resource;
    }
} 

支援多執行緒環境的語言,多半會有 Thread-Specific Storage 的方案,例如 Java 標準 API 就提供了 java.lang.ThreadLocal 可以使用。