導航:首頁 > 編程語言 > javastatic變數線程

javastatic變數線程

發布時間:2024-01-22 00:07:00

『壹』 java中多線程中的static和voilate

static不能實現多線程共享,它的作用是讓同一個類的不同對象,共用同一個變數,與線程無關。如果有多個線程同時修改一個靜態變數,同樣會有不同步問題。

『貳』 Java 理論與實踐: 正確使用 volatile 變數 線程同步

Java語言規范中指出 為了獲得最佳速度 允許線程保存共享成員變數的私有拷貝 而且只當線程進入或者離開同步代碼塊時才與共享成員變數的原始值對比

這樣當多個線程蔽握同時與某個對象交互時 就必須要注意到要讓線程及時的得到共享成員變數的變化

而volatile關鍵字就是提示VM:對於這個成員變數不能保存它的私有拷貝 而應直接與共享成員變數交互

使用建議 在兩個或者更多的線程訪問的成員變數上使用volatile 當要訪問慎並賣的變數已在synchronized代碼塊中 或者為常量時 不必使用

由於使用volatile屏蔽掉了VM中必要的代碼優化 所以在效率上比較低 因此一定在寬逗必要時才使用此關鍵字

Java的serialization提供了一種持久化對象實例的機制 當持久化對象時 可能有一個特殊的對象數據成員 我們不想用serialization機制來保存它 為了在一個特定對象的一個域上關閉serialization 可以在這個域前加上關鍵字transient

transient是Java語言的關鍵字 用來表示一個域不是該對象串列化的一部分 當一個對象被串列化的時候 transient型變數的值不包括在串列化的表示中 然而非transient型的變數是被包括進去的

注意static變數也是可以串列化的

Java 語言中的 volatile 變數可以被看作是一種 程度較輕的 synchronized ;與 synchronized 塊相比 volatile 變數所需的編碼較少 並且運行時開銷也較少 但是它所能實現的功能也僅是 synchronized 的一部分 本文介紹了幾種有效使用 volatile 變數的模式 並強調了幾種不適合使用 volatile 變數的情形

鎖提供了兩種主要特性 互斥(mutual exclusion) 和可見性(visibility) 互斥即一次只允許一個線程持有某個特定的鎖 因此可使用該特性實現對共享數據的協調訪問協議 這樣 一次就只有一個線程能夠使用該共享數據 可見性要更加復雜一些 它必須確保釋放鎖之前對共享數據做出的更改對於隨後獲得該鎖的另一個線程是可見的 如果沒有同步機制提供的這種可見性保證 線程看到的共享變數可能是修改前的值或不一致的值 這將引發許多嚴重問題

Volatile 變數

Volatile 變數具有 synchronized 的可見性特性 但是不具備原子特性 這就是說線程能夠自動發現 volatile 變數的最新值 Volatile 變數可用於提供線程安全 但是只能應用於非常有限的一組用例 多個變數之間或者某個變數的當前值與修改後值之間沒有約束 因此 單獨使用 volatile 還不足以實現計數器 互斥鎖或任何具有與多個變數相關的不變式(Invariants)的類(例如 start <=end )

出於簡易性或可伸縮性的考慮 您可能傾向於使用 volatile 變數而不是鎖 當使用 volatile 變數而非鎖時 某些習慣用法(idiom)更加易於編碼和閱讀 此外 volatile 變數不會像鎖那樣造成線程阻塞 因此也很少造成可伸縮性問題 在某些情況下 如果讀操作遠遠大於寫操作 volatile 變數還可以提供優於鎖的性能優勢

正確使用 volatile 變數的條件

您只能在有限的一些情形下使用 volatile 變數替代鎖 要使 volatile 變數提供理想的線程安全 必須同時滿足下面兩個條件

對變數的寫操作不依賴於當前值

該變數沒有包含在具有其他變數的不變式中

實際上 這些條件表明 可以被寫入 volatile 變數的這些有效值獨立於任何程序的狀態 包括變數的當前狀態

第一個條件的限制使 volatile 變數不能用作線程安全計數器 雖然增量操作(x++)看上去類似一個單獨操作 實際上它是一個由讀取 修改 寫入操作序列組成的組合操作 必須以原子方式執行 而 volatile 不能提供必須的原子特性 實現正確的操作需要使 x 的值在操作期間保持不變 而 volatile 變數無法實現這點 (然而 如果將值調整為只從單個線程寫入 那麼可以忽略第一個條件 )

大多數編程情形都會與這兩個條件的其中之一沖突 使得 volatile 變數不能像 synchronized 那樣普遍適用於實現線程安全 清單 顯示了一個非線程安全的數值范圍類 它包含了一個不變式 下界總是小於或等於上界

清單 非線程安全的數值范圍類

@NotThreadSafe

public class NumberRange {

private int lower upper;

public int getLower() { return lower; }

public int getUpper() { return upper; }

public void setLower(int value) {

if (value > upper)

throw new IllegalArgumentException(…)

lower = value;

}

public void setUpper(int value) {

if (value < lower)

throw new IllegalArgumentException(…)

upper = value;

}

}

這種方式限制了范圍的狀態變數 因此將 lower 和 upper 欄位定義為 volatile 類型不能夠充分實現類的線程安全 從而仍然需要使用同步 否則 如果湊巧兩個線程在同一時間使用不一致的值執行 setLower 和 setUpper 的話 則會使范圍處於不一致的狀態 例如 如果初始狀態是 ( ) 同一時間內 線程 A 調用 setLower( ) 並且線程 B 調用 setUpper( ) 顯然這兩個操作交叉存入的值是不符合條件的 那麼兩個線程都會通過用於保護不變式的檢查 使得最後的范圍值是 ( ) 一個無效值 至於針對范圍的其他操作 我們需要使 setLower() 和 setUpper() 操作原子化 而將欄位定義為 volatile 類型是無法實現這一目的的

性能考慮

使用 volatile 變數的主要原因是其簡易性 在某些情形下 使用 volatile 變數要比使用相應的鎖簡單得多 使用 volatile 變數次要原因是其性能 某些情況下 volatile 變數同步機制的性能要優於鎖

很難做出准確 全面的評價 例如 X 總是比 Y 快 尤其是對 JVM 內在的操作而言 (例如 某些情況下 VM 也許能夠完全刪除鎖機制 這使得我們難以抽象地比較 volatile和 synchronized 的開銷 )就是說 在目前大多數的處理器架構上 volatile 讀操作開銷非常低 幾乎和非 volatile 讀操作一樣 而 volatile 寫操作的開銷要比非 volatile 寫操作多很多 因為要保證可見性需要實現內存界定(Memory Fence) 即便如此 volatile 的總開銷仍然要比鎖獲取低

volatile 操作不會像鎖一樣造成阻塞 因此 在能夠安全使用 volatile 的情況下 volatile 可以提供一些優於鎖的可伸縮特性 如果讀操作的次數要遠遠超過寫操作 與鎖相比 volatile 變數通常能夠減少同步的性能開銷

正確使用 volatile 的模式

很多並發性專家事實上往往引導用戶遠離 volatile 變數 因為使用它們要比使用鎖更加容易出錯 然而 如果謹慎地遵循一些良好定義的模式 就能夠在很多場合內安全地使用 volatile 變數 要始終牢記使用 volatile 的限制 只有在狀態真正獨立於程序內其他內容時才能使用 volatile 這條規則能夠避免將這些模式擴展到不安全的用例

模式 # :狀態標志

也許實現 volatile 變數的規范使用僅僅是使用一個布爾狀態標志 用於指示發生了一個重要的一次性事件 例如完成初始化或請求停機

很多應用程序包含了一種控制結構 形式為 在還沒有準備好停止程序時再執行一些工作 如清單 所示

清單 將 volatile 變數作為狀態標志使用

volatile boolean shutdownRequested;

public void shutdown() { shutdownRequested = true; }

public void doWork() {

while (!shutdownRequested) {

// do stuff

}

}

很可能會從循環外部調用 shutdown() 方法 即在另一個線程中 因此 需要執行某種同步來確保正確實現 shutdownRequested 變數的可見性 (可能會從 JMX 偵聽程序 GUI 事件線程中的操作偵聽程序 通過 RMI 通過一個 Web 服務等調用) 然而 使用 synchronized 塊編寫循環要比使用清單 所示的 volatile 狀態標志編寫麻煩很多 由於 volatile 簡化了編碼 並且狀態標志並不依賴於程序內任何其他狀態 因此此處非常適合使用 volatile

這種類型的狀態標記的一個公共特性是 通常只有一種狀態轉換 shutdownRequested 標志從 false 轉換為 true 然後程序停止 這種模式可以擴展到來回轉換的狀態標志 但是只有在轉換周期不被察覺的情況下才能擴展(從 false 到 true 再轉換到 false) 此外 還需要某些原子狀態轉換機制 例如原子變數

模式 # :一次性安全發布(one time safe publication)

缺乏同步會導致無法實現可見性 這使得確定何時寫入對象引用而不是原語值變得更加困難 在缺乏同步的情況下 可能會遇到某個對象引用的更新值(由另一個線程寫入)和該對象狀態的舊值同時存在 (這就是造成著名的雙重檢查鎖定(double checked locking)問題的根源 其中對象引用在沒有同步的情況下進行讀操作 產生的問題是您可能會看到一個更新的引用 但是仍然會通過該引用看到不完全構造的對象)

實現安全發布對象的一種技術就是將對象引用定義為 volatile 類型 清單 展示了一個示例 其中後台線程在啟動階段從資料庫載入一些數據 其他代碼在能夠利用這些數據時 在使用之前將檢查這些數據是否曾經發布過

清單 將 volatile 變數用於一次性安全發布

public class BackgroundFloobleLoader {

public volatile Flooble theFlooble;

public void initInBackground() {

// do lots of stuff

theFlooble = new Flooble() // this is the only write to theFlooble

}

}

public class SomeOtherClass {

public void doWork() {

while (true) {

// do some stuff…

// use the Flooble but only if it is ready

if (floobleLoader theFlooble != null)

doSomething(floobleLoader theFlooble)

}

}

}

如果 theFlooble 引用不是 volatile 類型 doWork() 中的代碼在解除對 theFlooble 的引用時 將會得到一個不完全構造的 Flooble

該模式的一個必要條件是 被發布的對象必須是線程安全的 或者是有效的不可變對象(有效不可變意味著對象的狀態在發布之後永遠不會被修改) volatile 類型的引用可以確保對象的發布形式的可見性 但是如果對象的狀態在發布後將發生更改 那麼就需要額外的同步

模式 # :獨立觀察(independent observation)

安全使用 volatile 的另一種簡單模式是 定期 發布 觀察結果供程序內部使用 例如 假設有一種環境感測器能夠感覺環境溫度 一個後台線程可能會每隔幾秒讀取一次該感測器 並更新包含當前文檔的 volatile 變數 然後 其他線程可以讀取這個變數 從而隨時能夠看到最新的溫度值

使用該模式的另一種應用程序就是收集程序的統計信息 清單 展示了身份驗證機制如何記憶最近一次登錄的用戶的名字 將反復使用 lastUser 引用來發布值 以供程序的其他部分使用

清單 將 volatile 變數用於多個獨立觀察結果的發布

public class UserManager {

public volatile String lastUser;

public boolean authenticate(String user String password) {

boolean valid = passwordIsValid(user password)

if (valid) {

User u = new User()

activeUsers add(u)

lastUser = user;

}

return valid;

}

}

該模式是前面模式的擴展 將某個值發布以在程序內的其他地方使用 但是與一次性事件的發布不同 這是一系列獨立事件 這個模式要求被發布的值是有效不可變的 即值的狀態在發布後不會更改 使用該值的代碼需要清楚該值可能隨時發生變化

模式 # : volatile bean 模式

volatile bean 模式適用於將 JavaBeans 作為 榮譽結構 使用的框架 在 volatile bean 模式中 JavaBean 被用作一組具有 getter 和/或 setter 方法 的獨立屬性的容器 volatile bean 模式的基本原理是 很多框架為易變數據的持有者(例如 HttpSession)提供了容器 但是放入這些容器中的對象必須是線程安全的

在 volatile bean 模式中 JavaBean 的所有數據成員都是 volatile 類型的 並且 getter 和 setter 方法必須非常普通 除了獲取或設置相應的屬性外 不能包含任何邏輯 此外 對於對象引用的數據成員 引用的對象必須是有效不可變的 (這將禁止具有數組值的屬性 因為當數組引用被聲明為 volatile 時 只有引用而不是數組本身具有 volatile 語義) 對於任何 volatile 變數 不變式或約束都不能包含 JavaBean 屬性 清單 中的示例展示了遵守 volatile bean 模式的 JavaBean:

清單 遵守 volatile bean 模式的 Person 對象

@ThreadSafe

public class Person {

private volatile String firstName;

private volatile String lastName;

private volatile int age;

public String getFirstName() { return firstName; }

public String getLastName() { return lastName; }

public int getAge() { return age; }

public void setFirstName(String firstName) {

this firstName = firstName;

}

public void setLastName(String lastName) {

this lastName = lastName;

}

public void setAge(int age) {

this age = age;

}

}

volatile 的高級模式

前面幾節介紹的模式涵蓋了大部分的基本用例 在這些模式中使用 volatile 非常有用並且簡單 這一節將介紹一種更加高級的模式 在該模式中 volatile 將提供性能或可伸縮性優勢

volatile 應用的的高級模式非常脆弱 因此 必須對假設的條件仔細證明 並且這些模式被嚴格地封裝了起來 因為即使非常小的更改也會損壞您的代碼!同樣 使用更高級的 volatile 用例的原因是它能夠提升性能 確保在開始應用高級模式之前 真正確定需要實現這種性能獲益 需要對這些模式進行權衡 放棄可讀性或可維護性來換取可能的性能收益 如果您不需要提升性能(或者不能夠通過一個嚴格的測試程序證明您需要它) 那麼這很可能是一次糟糕的交易 因為您很可能會得不償失 換來的東西要比放棄的東西價值更低

模式 # :開銷較低的讀 寫鎖策略

目前為止 您應該了解了 volatile 的功能還不足以實現計數器 因為 ++x 實際上是三種操作(讀 添加 存儲)的簡單組合 如果多個線程湊巧試圖同時對 volatile 計數器執行增量操作 那麼它的更新值有可能會丟失

然而 如果讀操作遠遠超過寫操作 您可以結合使用內部鎖和 volatile 變數來減少公共代碼路徑的開銷 清單 中顯示的線程安全的計數器使用 synchronized 確保增量操作是原子的 並使用 volatile 保證當前結果的可見性 如果更新不頻繁的話 該方法可實現更好的性能 因為讀路徑的開銷僅僅涉及 volatile 讀操作 這通常要優於一個無競爭的鎖獲取的開銷

清單 結合使用 volatile 和 synchronized 實現 開銷較低的讀 寫鎖

清單 結合使用 volatile 和 synchronized 實現 開銷較低的讀 寫鎖 單 結合使用 volatile 和 synchronized 實現 開銷較低的讀 寫鎖

@ThreadSafe

public class CheesyCounter {

// Employs the cheap read write lock trick

// All mutative operations MUST be done with the this lock held

@GuardedBy( this ) private volatile int value;

public int getValue() { return value; }

public synchronized int increment() {

return value++;

}

}

之所以將這種技術稱之為 開銷較低的讀 寫鎖 是因為您使用了不同的同步機制進行讀寫操作 因為本例中的寫操作違反了使用 volatile 的第一個條件 因此不能使用 volatile 安全地實現計數器 您必須使用鎖 然而 您可以在讀操作中使用 volatile 確保當前值的可見性 因此可以使用鎖進行所有變化的操作 使用 volatile 進行只讀操作 其中 鎖一次只允許一個線程訪問值 volatile 允許多個線程執行讀操作 因此當使用 volatile 保證讀代碼路徑時 要比使用鎖執行全部代碼路徑獲得更高的共享度 就像讀 寫操作一樣 然而 要隨時牢記這種模式的弱點 如果超越了該模式的最基本應用 結合這兩個競爭的同步機制將變得非常困難

結束語

lishixin/Article/program/Java/hx/201311/25585

『叄』 java如何實現靜態變數多線程安全問題

public class A { public void method01(){ //dosomething } public void method02(){ //dosomething } //public void method03(){ public static void method03(){ // 靜態方法里才可以有今天變數 static Map map = new TreeMap(); static String x=「」; // 如果你的 method4,5,6是非同步的, 5,6得到的值就是不確定的, // 有可能是4之前的值,也有可能是4的賦值 method4(){ 這裡面會給map和x賦值 } method5(){ 這裡面會用到map和x } method6(){ 這裡面會用到map和x } }}

『肆』 JAVA中當多個線程同步調用static方法或static變數的時候會產生沖突嗎

靜態塊的資源訪問謹物信消時不會有沖突的,
單例么,比如車場是單例祥坦液的,但是裡面有很多車,每個客戶來都可以分配單獨的一輛車,所以多線程同步 也是不會沖突的

『伍』 java 線程無法修改全局變數。裡面str加不加static都不行。

之所以你列印出來的還是原始的值,這是因為操作系統在調度純程需要時間,所以當你的test.start()在起作用之前,你的system.out.printl(str)已經執行完成了。

你想看到你想要的結果的話,你可以在test.start();之後再加一句sleep(1000);。

閱讀全文

與javastatic變數線程相關的資料

熱點內容
數值積分演算法求pi 瀏覽:12
按鈕怎麼連命令方塊 瀏覽:907
房貸還清後不解壓能貸款嗎 瀏覽:34
程序員哄老婆開心技術 瀏覽:670
oracle自動備份壓縮文件 瀏覽:853
遵義人字形加密網帶 瀏覽:253
寧波人社在哪裡下載app 瀏覽:88
好噠商戶app下載在哪裡下載 瀏覽:607
廣發兌星巴克是在app哪裡操作 瀏覽:783
linuxgetline 瀏覽:215
app的懸浮窗許可權在哪裡設置 瀏覽:686
51單片機定時器溢出標志 瀏覽:373
單片機燒寫方法圖解 瀏覽:397
遍歷輸出java 瀏覽:551
貴妃app直播哪裡下載 瀏覽:273
android手機屏幕寬度和高度 瀏覽:816
不知道密碼怎麼強制解壓 瀏覽:181
疫情就是命令防控就是 瀏覽:872
linux查看存儲設備 瀏覽:245
stc1t單片機 瀏覽:315