Java多線程中static變量的使用
線程,是我們項目中繞不過的重點領域。提到線程,就常會聽到線程安全的術語。那什麽是線程安全呢?通俗點說,就是線程訪問時不產生資源沖突。其實,這是一個有點難以定義的概念,不是很容易讓人一聽就懂的概念。“一個類可以被多個線程安全調用就是線程安全的”《Java並發編程實踐》。
來說說靜態變量、實例變量、局部變量在多線程下的安全問題吧!
(一)靜態變量:線程非安全
1、靜態變量:使用static關鍵字定義的變量。static可以修飾變量和方法,也有static靜態代碼塊。被static修飾的成員變量和成員方法獨立於該類的任何對象。也就是說,它不依賴類特定的實例,被類的所有實例共享。只要這個類被加載,Java虛擬機就能根據類名在運行時數據區的方法區內定找到他們。因此,static對象可以在它的任何對象創建之前訪問,無需引用任何對象。
用public修飾的static成員變量和成員方法本質是全局變量和全局方法,當聲明它的類的對象時,不生成static變量的副本,而是類的所有實例共享同一個static變量。
2、靜態變量使用的場景:
(1)對象間共享值時
(2)方便訪問變量時
3、靜態方法使用註意事項:
(1)不能在靜態方法內使用非靜態變量,即不能直接訪問所屬類的實例變量;
(2)不能在靜態方法內直接調用非靜態方法;
(3)靜態方法中不能使用this和super關鍵字;
4、驗證靜態變量的線程安全性:
(1)從程序執行的圖中我們可以看出,執行結果中有錯誤數據,證明了靜態變量是存在資源沖突問題的。
(2)程序運行結果圖:
5、結論:靜態變量也稱為類變量,屬於類對象所有,位於方法區,為所有對象共享,共享一份內存,一旦值被修改,則其他對象均對修改可見,故線程非安全。
(二)實例變量:單例時線程非安全,非單例時線程安全
1、實例變量:實例變量屬於類對象的,也就是說,屬於對象實例私有,在虛擬機的堆中分配。
2、驗證實例變量的線程安全性:
(1)從程序截圖中,我們可以看到,當為單例模式時,會產生資源沖突,當非單例模式時,則不會產生線程沖突。
(2)程序運行結果圖:
圖1:
圖2:
3、結論:實例變量是實例對象私有的,系統只存在一個實例對象,則在多線程環境下,如果值改變後,則其它對象均可見,故線程非安全;如果每個線程都在不同的實例對象中執行,
則對象與對象間的修改互不影響,故線程安全。
(三)局部變量:線程安全
1、局部變量:定義在方法內部的變量。
2、驗證局部變量的安全性:
(1)從程序截圖中可以看出,局部變量在多線程下沒有產生資源沖突的問題
(2)程序運行結果圖:
3、結論:每個線程執行時都會把局部變量放在各自的幀棧的內存空間中,線程間不共享,故不存在線程安全問題。
(四)靜態方法的線程安全性
1、靜態方法中如果沒有使用靜態變量,則沒有線程安全的問題;
靜態方法內的變量,每個線程調用時,都會新創建一份,不會公用一個存儲單元,故不存在線程沖突的問題。
總結:
1. java在執行靜態方法時,會在內存中拷貝一份,如果靜態方法所在的類裏面沒有靜態的變量,那麽線程訪問就是安全的,比如在javaee中服務器必然會多線程的處理請求此時如果設計全局需要調用的靜態方法,可用此種設計。
2. java在執行靜態方法時,如果使用靜態變量,同時類的函數設計時使用到了靜態數據,最好在調用函數時使用synchronized關鍵字,否則會導致數據的不一致行。
3. 加靜態全局的變量,在多線程訪問下定會出現數據的不一致行,最好使用synchronized關鍵字,確保數據的一致性,典型的代表就是單例模式。
4. java是線程安全的,即對任何方法(包括靜態方法)都可以不考慮線程沖突,但有一個前提,就是不能存在全局變量。如果存在全局變量,則需要使用同步機制。
加入synchronized關鍵字的靜態方法稱為同步靜態方法。
在訪問同步靜態方法時,會獲取該類的“Class”對象,所以當一個線程進入同步的靜態方法中時,線程監視器獲取類本身的對象鎖,其它線程不能進入這個類的任何靜態同步方法。它不像實例方法,因為多個線程可以同時訪問不同實例同步實例方法。這個其實就是操作系統中的用信號量實現進程的互斥與同步問題,如果涉及在同一個類中有多個靜態方法中處理多線程共享數據的話,那就變成用信號量解決生產者-消費者問題。也就是說,靜態方法是一份臨界資源,對靜態方法的訪問屬於進入臨界區;對靜態變量的修改是一份臨界資源,對靜態變量的修改屬於進入臨界區。
參考:https://www.cnblogs.com/kabi/p/6889546.html
Java多線程中static變量的使用