1. 程式人生 > >ThreadLocal防止併發執行緒安全 執行緒隔離-ThreadLocalMap

ThreadLocal防止併發執行緒安全 執行緒隔離-ThreadLocalMap

Spring使用ThreadLocal解決執行緒安全問題我們知道在一般情況下,只有無狀態的Bean才可以在多執行緒環境下共享,在Spring中,絕大部分Bean都可以宣告為singleton作用域。就是因為Spring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非執行緒安全狀態採用ThreadLocal進行處理,讓它們也成為執行緒安全的狀態,因為有狀態的Bean就可以在多執行緒中共享了。

  一般的Web應用劃分為展現層、服務層和持久層三個層次,在不同的層中編寫對應的邏輯,下層通過介面向上層開放功能呼叫。在一般情況下,從接收請求到返回響應所經過的所有程式呼叫都同屬於一個執行緒,如圖9‑2所示:


  同一執行緒貫通三層這樣你就可以根據需要,將一些非執行緒安全的變數以ThreadLocal存放,在同一次請求響應的呼叫執行緒中,所有關聯的物件引用到的都是同一個變數。

  下面的例項能夠體現Spring對有狀態Bean的改造思路:

程式碼清單3 TestDao:非執行緒安全

[java] view plaincopyprint?派生到我的程式碼片
  1. package com.test;  
  2. import java.sql.Connection;  
  3. import java.sql.SQLException;  
  4. import java.sql.Statement;  
  5. publicclass
     TestDao {  
  6.     private Connection conn;// ①一個非執行緒安全的變數
  7.     publicvoid addTopic() throws SQLException {  
  8.         Statement stat = conn.createStatement();// ②引用非執行緒安全變數
  9.         // …
  10.     }  
  11. }  


由於①處的conn是成員變數,因為addTopic()方法是非執行緒安全的,必須在使用時建立一個新TopicDao例項(非singleton)。下面使用ThreadLocal對conn這個非執行緒安全的“狀態”進行改造:

程式碼清單4 TestDao:執行緒安全

[java] view plaincopyprint?派生到我的程式碼片
  1. package com.test;  
  2. import java.sql.Connection;  
  3. import java.sql.SQLException;  
  4. import java.sql.Statement;  
  5. publicclass TestDaoNew {  
  6.     // ①使用ThreadLocal儲存Connection變數
  7.     privatestatic ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();  
  8.     publicstatic Connection getConnection() {  
  9.         // ②如果connThreadLocal沒有本執行緒對應的Connection建立一個新的Connection,
  10.         // 並將其儲存到執行緒本地變數中。
  11.         if (connThreadLocal.get() == null) {  
  12.             Connection conn = getConnection();  
  13.             connThreadLocal.set(conn);  
  14.             return conn;  
  15.         } else {  
  16.             return connThreadLocal.get();// ③直接返回執行緒本地變數
  17.         }  
  18.     }  
  19.     publicvoid addTopic() throws SQLException {  
  20.         // ④從ThreadLocal中獲取執行緒對應的Connection
  21.         Statement stat = getConnection().createStatement();  
  22.     }  
  23. }  

  不同的執行緒在使用TopicDao時,先判斷connThreadLocal.get()是否是null,如果是null,則說明當前執行緒還沒有對應的Connection物件,這時建立一個Connection物件並新增到本地執行緒變數中;如果不為null,則說明當前的執行緒已經擁有了Connection物件,直接使用就可以了。這樣,就保證了不同的執行緒使用執行緒相關的Connection,而不會使用其它執行緒的Connection。因此,這個TopicDao就可以做到singleton共享了。

  當然,這個例子本身很粗糙,將Connection的ThreadLocal直接放在DAO只能做到本DAO的多個方法共享Connection時不發生執行緒安全問題,但無法和其它DAO共用同一個Connection,要做到同一事務多DAO共享同一Connection,必須在一個共同的外部類使用ThreadLocal儲存Connection。

ConnectionManager.java

[java] view plaincopyprint?派生到我的程式碼片
  1. package com.test;  
  2. import java.sql.Connection;  
  3. import java.sql.DriverManager;  
  4. import java.sql.SQLException;  
  5. publicclass ConnectionManager {  
  6.     privatestatic ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {  
  7.         @Override
  8.         protected Connection initialValue() {  
  9.             Connection conn = null;  
  10.             try {  
  11.                 conn = DriverManager.getConnection(  
  12.                         "jdbc:mysql://localhost:3306/test""username",  
  13.                         "password");  
  14.             } catch (SQLException e) {  
  15.                 e.printStackTrace();  
  16.             }  
  17.             return conn;  
  18.         }  
  19.     };  
  20.     publicstatic Connection getConnection() {  
  21.         return connectionHolder.get();  
  22.     }  
  23.     publicstaticvoid setConnection(Connection conn) {  
  24.         connectionHolder.set(conn);  
  25.     }  
  26. }  

java.lang.ThreadLocal<T>的具體實現

那麼到底ThreadLocal類是如何實現這種“為每個執行緒提供不同的變數拷貝”的呢?先來看一下ThreadLocal的set()方法的原始碼是如何實現的:

[java] view plaincopyprint?派生到我的程式碼片
  1. /** 
  2.     * Sets the current thread's copy of this thread-local variable 
  3.     * to the specified value.  Most subclasses will have no need to 
  4.     * override this method, relying solely on the {@link #initialValue} 
  5.     * method to set the values of thread-locals. 
  6.     * 
  7.     * @param value the value to be stored in the current thread's copy of 
  8.     *        this thread-local. 
  9.     */
  10.    publicvoid set(T value) {  
  11.        Thread t = Thread.currentThread();  
  12.        ThreadLocalMap map = getMap(t);  
  13.        if (map != null)  
  14.            map.set(this, value);  
  15.        else
  16.            createMap(t, value);  
  17.    }  

在這個方法內部我們看到,首先通過getMap(Thread t)方法獲取一個和當前執行緒相關的ThreadLocalMap,然後將變數的值設定到這個ThreadLocalMap物件中,當然如果獲取到的ThreadLocalMap物件為空,就通過createMap方法建立。


執行緒隔離的祕密,就在於ThreadLocalMap這個類。ThreadLocalMap是ThreadLocal類的一個靜態內部類,它實現了鍵值對的設定和獲取(對比Map物件來理解),每個執行緒中都有一個獨立的ThreadLocalMap副本,它所儲存的值,只能被當前執行緒讀取和修改。ThreadLocal類通過操作每一個執行緒特有的ThreadLocalMap副本,從而實現了變數訪問在不同執行緒中的隔離。因為每個執行緒的變數都是自己特有的,完全不會有併發錯誤。還有一點就是,ThreadLocalMap儲存的鍵值對中的鍵是this物件指向的ThreadLocal物件,而值就是你所設定的物件了。


為了加深理解,我們接著看上面程式碼中出現的getMap和createMap方法的實現:
[java] view plaincopyprint?派生到我的程式碼片
  1. /** 
  2.  * Get the map associated with a ThreadLocal. Overridden in 
  3.  * InheritableThreadLocal. 
  4.  * 
  5.  * @param  t the current thread 
  6.  * @return the map 
  7.  */
  8. ThreadLocalMap getMap(Thread t) {  
  9.     return t.threadLocals;  
  10. }  
  11. /** 
  12.  * Create the map associated with a ThreadLocal. Overridden in 
  13.  * InheritableThreadLocal. 
  14.  * 
  15.  * @param t the current thread 
  16.  * @param firstValue value for the initial entry of the map 
  17.  * @param map the map to store. 
  18.  */
  19. void createMap(Thread t, T firstValue) {  
  20.     t.threadLocals = new ThreadLocalMap(this, firstValue);  
  21. }  

接下來再看一下ThreadLocal類中的get()方法:
[java] view plaincopyprint?派生到我的程式碼片
  1. /** 
  2.  * Returns the value in the current thread's copy of this 
  3.  * thread-local variable.  If the variable has no value for the 
  4.  * current thread, it is first initialized to the value returned 
  5.  * by an invocation of the {@link #initialValue} method. 
  6.  * 
  7.  * @return the current thread's value of this thread-local 
  8.  */
  9. public T get() {  
  10.     Thread t = Thread.currentThread();  
  11.     ThreadLocalMap map = getMap(t);  
  12.     if (map != null) {  
  13.         ThreadLocalMap.Entry e = map.getEntry(this);  
  14.         if (e != null)  
  15.             return (T)e.value;  
  16.     }  
  17.     return setInitialValue();  
  18. }  

再來看setInitialValue()方法: [java] view plaincopyprint?派生到我的程式碼片
  1. /** 
  2.     * Variant of set() to establish initialValue. Used instead 
  3.     * of set() in case user has overridden the set() method. 
  4.     * 
  5.     * @return the initial value 
  6.     */
  7.    private T setInitialValue() {  
  8.        T value = initialValue();  
  9.        Thread t = Thread.currentThread();  
  10.        ThreadLocalMap map = getMap(t);  
  11.        if (map != null

    相關推薦

    ThreadLocal防止併發執行安全 執行隔離-ThreadLocalMap

    Spring使用ThreadLocal解決執行緒安全問題我們知道在一般情況下,只有無狀態的Bean才可以在多執行緒環境下共享,在Spring中,絕大部分Bean都可以宣告為singleton作用域。就是因為Spring對一些Bean(如RequestContextHold

    鎖,執行安全,執行池,threading.local,生產者,消費者模型

    一丶鎖 1.鎖:LOCK(一次放一個) 執行緒安全,多執行緒操作的時候,內部會讓所有執行緒排隊處理,如list/dict/Queue 執行緒不安全 + 人 => 排隊處理 需求:   a.建立100個執行緒,在列表中追加8   b.建立100個執行緒     v = []     鎖

    併發程式設計:執行安全ThreadLocal

    執行緒安全的概念:當多個執行緒訪問某一個類(物件或方法)時,這個類始終都能表現出正確的行為,那麼這個類(物件或方法)就是執行緒安全的。 執行緒安全 說的可能比較抽象,下面就以一個簡單的例子來看看什麼是執行緒安全問題。 public class MyThread impleme

    Java併發程式設計:執行安全ThreadLocal

    執行緒安全的概念:當多個執行緒訪問某一個類(物件或方法)時,這個類始終都能表現出正確的行為,那麼這個類(物件或方法)就是執行緒安全的。 執行緒安全 說的可能比較抽象,下面就以一個簡單的例子來看看什麼是執行緒安全問題。 public class MyThread

    ThreadLocal-單例模式下高併發執行安全

    MirrorThread: public class MirrorThread extends Thread{ private Mirror mirror; private String threadName; public MirrorThread(Mirror mirror, Stri

    Spring-利用ThreadLocal解決執行安全問題(多執行併發登入)

    ThreadLocal是什麼ThreadLocal,顧名思義,它不是一個執行緒,而是執行緒的一個本地化物件。當工作於多執行緒中的物件使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒分配一個獨立的變數副本。所以每一個執行緒都可以獨立地改變自己

    併發程式設計之多執行執行安全

    什麼是執行緒安全? 為什麼有執行緒安全問題? 當多個執行緒同時共享,同一個全域性變數或靜態變數,做寫的操作時,可能會發生資料衝突問題,也就是執行緒安全問題。但是做讀操作是不會發生資料衝突問題。 案例: 需求現在有100張火車票,有兩個視窗同時搶火車票,請使用多執行緒模擬搶票效果。 p

    Java併發理論基礎—執行安全策略

    不可變物件需要滿足的條件: 1、物件建立以後其狀態就不能修改 2、物件所有域都是final型別 3、物件是正確建立的(在物件建立期間,this引用沒有逸出) final關鍵字:類、方法、變數 修飾類:不能被繼承 修飾方法:1、鎖定方法不被繼承類修改;2、效率 修飾變數:基

    Java併發程式設計(1)-執行安全基礎概述

    文章目錄 一、執行緒安全性 1.1、無狀態類 1.2、有狀態類 二、原子性 2.1、原子操作 2.2、競爭操作 2.3、複合操作

    Java併發程式設計之執行安全執行通訊

    Java多執行緒開發中最重要的一點就是執行緒安全的實現了。所謂Java執行緒安全,可以簡單理解為當多個執行緒訪問同一個共享資源時產生的資料不一致問題。為此,Java提供了一系列方法來解決執行緒安全問題。 synchronized synchronized用於同步多執行緒對共享資源的訪問,在實現中分為同步程

    java併發程式設計一一多執行執行安全(四)

    ##1.java重排序 ###1.1資料依賴性 如果兩個操作訪問同一個變數時,且這兩個操作匯中有一個為寫操作,此時這兩個操作之間就 存在資料依賴性。資料依賴分下列三種類型。 名稱 程式碼示例 說明

    java併發程式設計一一多執行執行安全(三)

    1.多執行緒的三大特性 1.1什麼是原子性 即一個操作或多個操作要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。 一個很經典的例子就是銀行賬戶轉賬問題: 比如從賬戶A向賬戶B轉1000元,那麼必然包括2個操作:從賬戶A減去1000元,往賬戶B加上1000元。這2

    java併發程式設計一一多執行執行安全(二)

    1.多執行緒死鎖 1.1什麼是多執行緒死鎖? 同步中巢狀同步,導致鎖無法釋放 程式碼示例: class Thread009 implements Runnable { private int trainCount = 100; private Object

    java併發程式設計一一多執行執行安全(一)

    1.什麼是執行緒安全? 1.1為什麼有執行緒安全問題? 當多個執行緒同時共享同一個全域性變臉或靜態變數,做寫的操作時,可能會發生資料衝突的問題, 也就是執行緒安全的問題。但是做讀操作是不會發生資料衝突問題。 舉例:現在有100張火車票,有兩個視窗同時搶火車票,用多執行緒模擬搶

    ThreadLocal解決事務執行安全問題(c3p0資料庫連線池工具類)

    ThreadLocal底層是Map集合,它的key是當前執行緒,value由自己設定,可以繫結Connection或其他物件等,保證本次同一執行緒使用同一Connection。 ThreadLocal類提供幾個方法: get/set/remove 以下是ThreadLocal搭配c3p

    Java Web併發訪問的執行安全問題

    Java Web併發訪問的執行緒安全問題 2018年05月12日 02:02:52 菜鳥級的IT之路 閱讀數:68更多 個人分類: JAVA~JavaWeb 一、Servlet的執行緒安全問題 Java web伺服器下,每個Servlet只有一個例項(即

    Java併發程式設計(3)-構造執行安全類的模式

    文章目錄 一、例項限制模式 1.1、 限制變數確保執行緒安全 1.2、分析ArrayList的執行緒安全性 1.3、總結 二、委託執行緒安全模式 2.

    《Java併發程式設計實踐——第一章(介紹)、第二章(執行安全)》

    介紹## 1.1 併發的簡短歷史 相同的關注點(資源利用,公平和方便) 不僅促進了程序的發展,也促進了執行緒的發展、 執行緒允許程式控制流的多重分支同時存在於一個程序。它們共享程序範圍內的資源,比如記憶體和檔案控制代碼,但是執行緒有自己的程式計數器、棧、和本地變數。 1.2

    併發程式設計實戰(5): 不變性、final與執行安全

    不變性與final欄位 如果某個物件在被建立後其狀態就不能被修改,那麼這個物件就被成為不可變物件。 在Java中,final型別的域是不能修改的。 不可變性不等於將物件中的所有域都宣告為final型別,即使物件中的所有域都是final型別的,這個物件仍然是可變的,因為在fin

    ThreadLocal實現執行安全

    Spring通過各種模板類降低了開發者使用各種資料持久技術的難度。這些模板類都是執行緒安全的,也就是說,多個DAO可以複用同一個模板例項而不會發生衝突。我們使用模板類訪問底層資料,根據持久化技術的不同,模板類需要繫結資料連線或會話的資源。但這些資源本身是非執行緒安全的,也就是說它們不能在同一時刻被多