線程安全處理之Threadlocal

今天項目中遇到一個問題:利用SecurityContextHolder獲取用戶登陸信息的時候一直報空指針異常。網上查詢原來這是一個線程級別的全局變量,只能在主線程上訪問。java

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

概述

Threadlocal是Thread的局部變量,它爲每一個使用該變量的線程提供獨立的變量副本。每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本,最終目的是實現線程之間的數據隔離(有個關鍵字叫Synchronized是實現線程之間的數據隔離)。ide

ThreadLocal提供的四個方法做用以下:spa

  • 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線程無影響
    }
}




複製代碼
相關文章
相關標籤/搜索