執行緒安全處理之Threadlocal

玉獅子發表於2019-04-29

今天專案中遇到一個問題:利用SecurityContextHolder獲取使用者登入資訊的時候一直報空指標異常。網上查詢原來這是一個執行緒級別的全域性變數,只能在主執行緒上訪問。

在Spring中,對例如如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等非執行緒安全狀態採用ThreadLocal進行處理,讓它們也成為執行緒安全的狀態。

概述

Threadlocal是Thread的區域性變數,它為每個使用該變數的執行緒提供獨立的變數副本。每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本,最終目的是實現執行緒之間的資料隔離(有個關鍵字叫Synchronized是實現執行緒之間的資料隔離)。

ThreadLocal提供的四個方法作用如下:

  • initialValue() //返回該執行緒區域性變數的初始值 ,在get操作沒有對應的值時,呼叫此方法
  • get() //返回當前執行緒所對應的執行緒變數副本
  • set(Object value)//設定當前執行緒的執行緒變數副本
  • remove() //將當前執行緒變數副本的值刪除

原理

介紹原理之前需要了解另外兩個類:

  • Thread //用於操作執行緒 ,內部有ThreadLocalThreadLocalMap屬性
  • ThreadLocalMap //ThreadLocal內部類,用來儲存資料,儲存了以threadLocal為key,需要隔離的資料為value。

Thread內有個threadLocals,該屬性用來儲存該執行緒本地變數。ThreadLocal進行set()和get()操作時都要首先獲取當前執行緒,然後獲取執行緒內的threadLocals,如果threadLocals存在,則以threadlocal為key調取vlaue或set值。這樣每個執行緒都有自己的資料,就做到了不同執行緒間資料的隔離,保證了資料安全。

public
class Thread implements Runnable {
 
    ThreadLocal.ThreadLocalMap threadLocals = null;
 
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 
複製程式碼

案例

public class ThreadLocalTest {
    private static String s1;
    private static ThreadLocal<String> s2 = new ThreadLocal<>();

    public static void main(String[] args) {
        s1 = "test1";
        threadLabel.set("test1");
        //開啟一個新執行緒,改變s1,s2的值
        Thread thread = new Thread() {
            @Override
            public void run() {
                s1= "test2";
                s2.set("test2");
            }
        };
        thread.start();

        System.out.println("s1值為" +s1 );//獲取到s1變化為為test2
        System.out.println("s2值為" +s2.get());//獲取到的s2仍然為為test1,在另外執行緒賦值對main執行緒無影響
    }
}




複製程式碼

相關文章