導航:首頁 > 編程語言 > java同步變數

java同步變數

發布時間:2023-04-20 07:54:52

1. java中線程同步的幾種方法

線程同步主要有以下種方法(示例中是實現計數的功能):

1、同步方法,即使用synchronized關鍵字修飾方法,例如:

publicsynchronizedvoidadd(intc){...}

2、同步代碼塊,即有synchronized關鍵字修飾的語句塊,例如:

publicvoidaddAndGet(intc){
synchronized(this){
count+=c;
}
}

3、使用特殊域變數(volatile)實現線程同步,該方法不能保證絕對的同步。

例如:privatevolatileintcount=0;

4、使用鎖實現線程同步,例如:

privateLocklock=newReentrantLock();
publicvoidadd(intc){
lock.lock();//上鎖
try{
count+=c;
}finally{
lock.unlock();//解鎖
}
}

5、使用原子變數實現線程同步,在java的util.concurrent.atomic包中提供了創建了原子類型變數的工具類,例如:

privateAtomicIntegercount=newAtomicInteger(1);
publicvoidadd(intc){
count.addAndGet(c);
}

6、使用局部變數實現線程同步,如果使用ThreadLocal管理變數,則每一個使用該變數的線程都獲得該變數的副本, 副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變數副本,而不會對其他線程產生影響。

ThreadLocal 類的常用方法

new ThreadLocal<T>() : 創建一個線程本地變數

get() : 返回此線程局部變數的當前線程副本中的值

initialValue() : 返回此線程局部變數的當前線程的"初始值"

set(T value) : 將此線程局部變數的當前線程副本中的值設置為value

示例代碼:

privatestaticThreadLocal<Integer>count=newThreadLocal<Integer>(){
@Override
protectedIntegerinitialValue(){
return1;
}
};

publicvoidadd(intc){
count.set(count.get()+c);
}

7、使用阻塞隊列實現,例如LinkedBlockingQueue,具體使用可網路LinkedBlockingQueue的用法或查看java文檔。

2. 用java語言同步實現兩個線程,一個使某個變數加1,另一個是某個變數減1,循環50次

public class Test {
/** 要變搭辯化的變知敬缺稿歲量 */
private static int num = 0;
public static void main(String[] args) {
//線程一
Thread t1 = new Thread(new Runnable(){
public void run() {
//變數加一50次
for(int i = 0; i < 50; i ++){
num ++;
}
}
});
//線程二
Thread t2 = new Thread(new Runnable(){
public void run() {
//變數減一50次
for(int i = 0; i < 50; i ++){
num --;
}
}
});

//啟動兩個線程
t1.start();
t2.start();
}
}

3. Java語言包含兩哪些同步機制

Java 語言包含兩種內在的同步機制(都是為了實現代碼線程的安全性):

(1)同步塊(或方法)。即被synchronized修飾的變數和方法。

(2)volatile 變數。Volatile 變數的同步性較差(但有時它更簡單並且開銷更低),而且其使用也更容易出錯。Java 語言中的 volatile 變數可以被看作是一種 「程度較輕的synchronized」;與synchronized塊相比,volatile 變數所需的編碼較少,並且運行時開銷也較少,但是它所能實現的功能也僅是synchronized的一部分。

正確使用volatile的條件:

對變數的寫操作不依賴於當前值。 該變數沒有包含在具有其他變數的不變式中。

條件解釋:

在使用volatile關鍵字時要慎 重,並不是只要簡單類型變數使用volatile修飾,對這個變數的所有操作都是原來操作,當變數的值由自身的上一個決定時,如n=n+1、n++ 等,volatile關鍵字將失效,只有當變數的值和自身上一個值無關時對該變數的操作才是原子級別的,如n = m + 1,這個就是原級別的。所以在使用volatile關鍵時一定要謹慎,如果自己沒有把握,可以使用synchronized來代替volatile。

4. Java多線程初學者指南(12):使用Synchronized塊同步變數

我們可以通過synchronized塊來同步特定的靜態或非靜態方法 要想實現這種需求必須為這些特性的方法定義一個類變數 然後將這些方法的代碼用synchronized塊括起來 並將這個類變數作為參數傳入synchronized塊 下面的代碼演示了如何同步特定的類方法

package mythread; publicclass SyncThread extendsThread { privatestaticStringsync= ; privateStringmethodType= ; privatestaticvoidmethod(Strings) { synchronized(sync) { sync=s; System out println(s); while(true); } } publicvoidmethod () { method( method ); } publicstaticvoidstaticMethod () { method( staticMethod ); } publicvoidrun() { if(methodType equals( static )) staticMethod (); elseif(methodType equals( nonstatic )) method (); } public SyncThread(StringmethodType) { thodType=methodType; } publicstaticvoidmain(String[]args)throwsException { SyncThread sample =new SyncThread( nonstatic ); SyncThread sample =new SyncThread( static ); sample start(); sample start(); } }

運行結果如下

method staticMethod

看到上面的運行結果很多讀者可能感到驚奇 在上面的代碼中method 和staticMethod 方法使用了靜態字元串變數sync進行同步 這兩個方法只能有一個同時執行 而這兩個方法都會執行 行的頃塵亂無限循環語句 因此 輸出結果只能是method 和staticMethod 其中之一 但這個程序將這兩個字元串都輸出了

出現這種結果的願意很簡單 我們看一下 行就知道了 原來在這一行將sync的值改變了 在這里要說一下Java中的String類型 String類型和Java中其他的復雜類型不同 在使用String型變數時 只要給這個變數賦一次值 Java就會創建個新的String類型的實例 如下面的代碼所示

Strings= hello ;System out println(s hashCode());s= world ;System out println(s hashCode());

在上面的代碼中 第一個s和再次賦值後的s的hashCode的值是不一樣的雀檔 由於創建String類的實例並不需要使用new 因此 在同步String類型的變數時要注意不要給這個變數賦值 否則會使變數無法同步

由於在 行已經為sync創建了一個新的實例 假設method 先執行 當method 方法執行了 行的代碼後 sync的值就已經不是最初那個值了 而method 方法鎖定的仍然是sync變數最初的那個值 而在這時 staticMethod 正好執行到synchronized(sync) 在staticMethod 方法中要鎖定的這個sync和method 方法鎖定的sync已經不是一個了 因此 這兩個方法的同步性已經被破壞了

解決以上問題的方法當然是將 行去掉 在本例中加上這行 只是為了說明使用類變數來同步方法時如果在synchronized塊中將同步變數的值改變 就會破壞方法之間的同步 為了徹底避免這種情況發生 在定義同步變數時可以使用final關鍵字 如將上面的程兄大序中的 行可改成如下形式

privatefinalstaticStringsync= ;

使用final關鍵字後 sync只能在定義時為其賦值 並且以後不能再修改 如果在程序的其他地方給sync賦了值 程序就無法編譯通過 在Eclipse等開發工具中 會直接在錯誤的地方給出提示

我們可以從兩個角度來理解synchronized塊 如果從類方法的角度來理解 可以通過類變數來同步相應的方法 如果從類變數的角度來理解 可以使用synchronized塊來保證某個類變數同時只能被一個方法訪問 不管從哪個角度來理解 它們的實質都是一樣的 就是利用類變數來獲得同步鎖 通過同步鎖的互斥性來實現同步

lishixin/Article/program/Java/gj/201311/27400

5. Java怎麼使用synchronized聲明一個變數

首先要說明的是,java里不能直接使用synchronized聲明一個變數,而是使用synchronized去修飾一個代碼塊或一個方法。

詳細說明如下:

synchronized用來修飾一個方法或者一個代碼塊,它用來保證在同一時刻最多隻有一個線程執行該段代碼。
一、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。
二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
三、尤其關鍵的是,當一個線程訪問object的亮握一個synchronized(this)同步代碼塊時,其他線程對object中所亂核有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。敬陪慶
五、以上規則對其它對象鎖同樣適用。

示例代碼:
public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
結果:
A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4

6. java多線程同步全局變數

1.使用JAVA Collections 這個類. 有 checkedList / Map / Set 方法. 將你的集合放進去,會返回給你一個線程安全的集合. 這樣不需要你手動去做線程同步, java已經幫你做了.

2.使用 synchronized 關鍵字, 同步 你的刪除修改操作.

3.使用 synchronized 修飾方法. 將修改刪除的方法加鎖.

7. java 多線程 同時操作一個變數 高分懸賞

ArrayList不是線程安全的野遲 所以 synchronized 必須有 這一點是關鍵,其他的都是浮雲。還有 兩個線程sleep一會更好 否則 這個猛喊跟死循環一樣了 機器受不了啊!。
public class Test {

public static List<String> list = new ArrayList<String>();

public static void main(String[] args) {
myThreadClass1 thread1 = new myThreadClass1();
myThreadClass2 thread2 = new myThreadClass2();
Thread t1 = new Thread(thread1);
Thread t2 = new Thread(thread2);
t1.start();
t2.start();
}
}

class myThreadClass1 implements Runnable {
public void run() {
while (true) {//枝脊野 這就不要寫1 ==1 了
synchronized (Test.class) {
System.out.println("add!!!!");
Test.list.add("123");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

class myThreadClass2 implements Runnable {
public void run() {
while (true) {
synchronized (Test.class) {
Iterator it = Test.list.iterator();
// 循環里remove會出沖突異常的
List list2 = new ArrayList();
while (it.hasNext()) {
Object obj = it.next();
System.out.println("remove:" + obj);
list2.add(obj);
}
// you can do anything with list2
// avoid java.util.
Test.list.clear();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

8. java多線程中,如何給靜態變數(如List)加鎖/同步

使用synchronized關鍵字同步方法就可以了。
public class Foo2 {
private int x = 100;
public int getX() {
return x;
}
//同步方法
public synchronized int fix(int y) {
x = x - y;
System.out.println("線程"+Thread.currentThread().getName() + "運行結束,減少「" + y + "」,當前值為:" + x);
return x;
}
}

9. Java中對線程間的變數訪問也需要同步控制

一個簡單的計數器 本來以為不需要同步保護 後來發現不行 還是得加上 程序

public class TestMain {

int i = ; //計數器初始值為

public static void main(String[] args) {

TestMain c = new TestMain();

Worker x = new Worker(c);

for (int i= ; i< ; i++) { // 個線程

new Thread(x) start();

}

while (true) { //每隔一秒中輸出計數器的值

System out println(c i);

try {

Thread sleep( );

} catch (InterruptedException e) {

}

}

}

}

class Worker implements Runnable {

TestMain c;

public Worker(TestMain c) {

this c = c;

}

public void run() {

try {

Thread sleep((int)(Math random() * )); //隨機Sleep一段時間

} catch (InterruptedException e) {

}

螞銷橘c i++; //計數器自增 問題在悶團這里 並發寫入

}

}

上面的程序 %的幾率結果是 其餘的是

c i++一句需要並發保護

斗棚本來我以為Java裡面++是原子的呢 呵呵

lishixin/Article/program/Java/gj/201311/11157

10. 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同步變數相關的資料

熱點內容
阿里雲伺服器能連接列印機嗎 瀏覽:169
命令行參考 瀏覽:279
怎麼初步認識編程 瀏覽:208
為什麼程序員都喜歡谷歌 瀏覽:891
壓縮性骨拆能自愈嗎 瀏覽:277
安卓怎麼設置游戲畫面 瀏覽:114
k線上寫字源碼 瀏覽:457
單擊按鈕保存資料源碼 瀏覽:354
華為gt加密卡 瀏覽:213
河北超融合伺服器廠家雲主機 瀏覽:894
芙兒優安全座椅app怎麼連接 瀏覽:294
專業美團騎手app怎麼開通 瀏覽:949
個人音樂分享網站源碼 瀏覽:375
在新電腦上怎麼注冊加密狗 瀏覽:123
最後一戰游戲源碼 瀏覽:5
phpmysql實例下載 瀏覽:751
傳智黑馬安卓非加密 瀏覽:553
伺服器如何配置host 瀏覽:1001
守望執行命令 瀏覽:371
加密狗插上去了怎麼辦 瀏覽:624