java併發和高併發之線程封閉

一、線程封閉:

1、概述;線程安全策略,除了不可變對象,還有一個簡單的方法就是線程封閉。線程封閉其實就是把對象封裝到一個線程裏,只有這一個線程能看到這個對象,那麼這個對象本身就算不是線程安全的,也不會出現任何線程安全問題,因爲它只能出現在一個線程裏。

2、實現線程封閉的幾個方法:

》Ad-hoc 線程封閉:完全靠程序控制實現,非常脆弱,最糟糕,忽略;

》堆棧封閉:局部變量,無併發問題。被使用的最多的方法,因爲多個線程訪問一個方法時,方法的局部變量都會被拷貝一份到線程的棧中,所以局部變量是不會被多個線程所共享的,因此不會出現併發問題。可以查看java內存模型。所以能用局部變量時,不要使用全局變量。

》ThreadLocal線程封閉:特別好的封閉方法。ThreadLocal中維護了一個map,其中線程的名稱作爲key,要封閉的對象作爲value.每一個線程中的對象都對應map中的一個值。也就是說,ThreadLocal利用map 實現了線程封閉。

3、代碼示例threadLocal的用法:

1)示例場景:threadLocal結合loginFilter實現登錄請求攔截功能。

方法:將用戶信息存放到threadLocal中,在請求對應的線程,任何有需要獲取當前登錄用戶信息的地方,都可以方便地取出。由於threadLocal是線程間封閉的,也不需要擔心任何線程安全問題。

   對於服務器而言,任何一個用戶請求都是一個線程。我們希望線程間隔離。通過threadLocal和loginFilter 過濾器結合,可以實現在接口處理之前先取出相關的信息,在接口實際使用時,何時需要,何時獲取。而避免了每次請求要傳遞用戶基本信息,可能要傳遞很多層的麻煩,代碼臃腫和其他安全性等問題。

2)代碼演示過程。

》先創建一個RequestHolder,用於控制(操作)想要綁定到threadLocal中的信息。在其中聲明一個threadLocal對象

爲該類創建一個添加存放對象的方法add()  、一個獲取該對象的getId()方法,最後不要忘記創建一個移除存放到threadLocal中信息的remove()  方法。

如上,實質就是一個操作threadLocal的工具類,包括,向threadLocal中添加、查詢、移除信息的方法。

那,如上方法何時調用呢?

add 當(比如登錄)請求訪問到服務器,但沒有實際處理時,調用該方法,被攔截器攔截,在攔截器中將相應信息寫入到threadLocal中。當再次有請求實際處理,需要獲取信息時,調用get方法,而當請求 實際處理完結束時,將請求存儲的信息從threadLocal中移除。即調用remove方法。此時我們容易想到intercepter中 調用方法前和後分別執行的操作。

先書寫過濾器:

創建Intercepter:

在application中配置interceptor  和 過濾器 :

寫Controller接口進行驗證:

運行項目,通過瀏覽器或者postman等方式訪問controller層的訪問接口,

 註上方的整個springboot 中,使用threadLocal 的代碼運行流程:

使用threadLocal時定義了三個方法,分別是存放 獲取 移除數據。存放數據通常是在過濾器(攔截器)中,先攔截住請求,然後存放數據。最後在interceptor中移除數據,防止內存泄露。在做接口處理時,可以實時地獲取數據。

4、threadLocal 是線程安全的,使用比較容易。

5、擴展,線程封閉技術還有一個常用的,即jdbc中連接數據庫的操作對象Connection, 該對象本身是一個線程不安全的對象,但是在服務器實際應用程序中,因爲線程從連接池獲取了一個Connection 對象,使用完之後返回給連接池,由於大多數請求是通過單線程採用同步進行處理的,並且在Connection對象返回連接池之前,不會將它分配給其他線程,因此這種管理方式,將連接請求隱式地存放在線程封閉之內,如此實現了,雖然Connection對象本身不是線程安全的,但是通過線程封閉做到了線程安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章