原帖收藏於IT老兵博客。html
這裏翻譯一篇對Java的synchronized關鍵字的講解文章,這個關鍵字用於解決Java世界裏面競爭條件時的訪問衝突問題。java
Java synchronized塊將方法或代碼塊標記爲已同步。 Java synchronized塊可用於避免競爭條件。web
Java中的同步塊使用synchronized關鍵字標記。Java中的同步塊在某個對象上同步。在同一對象上的全部同步塊在同一時刻只能被一個線程執行。嘗試進入同步塊的全部其餘線程將被阻塞,直到同步塊內的線程退出塊。併發
synchronized關鍵字可用於標記四種不一樣類型的塊:svg
這些塊在不一樣對象上同步,您須要哪一種類型的同步塊取決於具體狀況。函數
這是一個同步的實例方法:this
public synchronized void add(int value){ this.count + = value; }
請注意在方法聲明中使用synchronized關鍵字,這告訴Java該方法是同步的。線程
Java中的同步實例方法在擁有該方法的實例(對象)上同步。所以,每一個實例的同步方法在不一樣的對象上同步:擁有它的實例。在一個同步實例方法中,只有一個線程能夠執行。若是存在多個實例,則一次只能有一個線程能夠在每一個實例的同步實例方法內執行。每一個實例一個線程。翻譯
靜態方法被標記爲synchronized,就像使用synchronized關鍵字的實例方法同樣。這是一個Java synchronized靜態方法示例:code
public static synchronized void add(int value){ count + = value; }
此處,synchronized關鍵字告訴Java該方法已同步。
同步靜態方法在同步靜態方法所屬的類的類對象上同步。因爲每一個類在Java VM中只存在一個類對象,所以在同一個類中的靜態同步方法中只能執行一個線程。
若是靜態同步方法位於不一樣的類中,則一個線程能夠在每一個類的靜態同步方法內執行。每一個類一個線程,不管它調用哪一個靜態同步方法。
您不須要同步整個方法,有時只須要同步方法的一部分。方法中的Java同步塊使這成爲可能。
如下是非同步Java方法中的同步Java代碼塊:
public void add(int value){ synchronized(this){ this.count + = value; } }
此示例使用Java synchronized塊構造將代碼塊標記爲已同步。此代碼如今將像執行同步方法同樣執行。
請注意Java synchronized塊構造如何在括號中獲取對象。在示例中使用「this」,這是調用add方法的實例。由synchronized構造在括號中獲取的對象稱爲監視器對象。聽說代碼在監視器對象上同步。同步實例方法使用它所屬的對象做爲監視對象。
只有一個線程能夠在同一監視器對象上同步的Java代碼塊內執行。
如下兩個示例在調用它們的實例上同步。所以,它們在同步方面是等效的:
public class MyClass { public synchronized void log1(String msg1,String msg2){ log.writeln(MSG1); log.writeln(MSG2); } public void log2(String msg1,String msg2){ synchronized(this){ log.writeln(MSG1); log.writeln(MSG2); } } }
所以,在該示例中,只有單個線程能夠在兩個同步塊中的任一個內執行。
若是第二個同步塊在與此不一樣的對象上同步,則一次一個線程就可以在每一個方法內執行。
如下是與靜態方法相同的兩個示例。這些方法在方法所屬的類的類對象上同步:
public class MyClass { public static synchronized void log1(String msg1,String msg2){ log.writeln(MSG1); log.writeln(MSG2); } public static void log2(String msg1,String msg2){ synchronized(MyClass.class){ log.writeln(MSG1); log.writeln(MSG2); } } }
只有一個線程能夠同時在這兩個方法中的任何一個內執行。
若是第二個同步塊在與MyClass.class不一樣的對象上同步,那麼一個線程能夠同時在每一個方法內執行。
下面是一個示例,它啓動2個線程並讓它們在同一個Counter實例上調用add方法。 一次只有一個線程可以在同一個實例上調用add方法,由於該方法在它所屬的實例上是同步的。
public class Counter { long count = 0; public synchronized void add(long value) { this.count += value; } }
public class CounterThread extends Thread { protected Counter counter = null; public CounterThread(Counter counter) { this.counter = counter; } public void run() { for (int i = 0; i < 10; i++) { counter.add(i); } } }
public class Example { public static void main(String[] args) { Counter counter = new Counter(); Thread threadA = new CounterThread(counter); Thread threadB = new CounterThread(counter); threadA.start(); threadB.start(); } }
建立了兩個線程。 相同的Counter實例在其構造函數中傳遞給它們。 Counter.add()方法在實例上同步,由於add方法是一個實例方法,並標記爲synchronized。 所以,只有一個線程能夠一次調用add()方法。 另外一個線程將等到第一個線程離開add()方法,而後才能執行方法自己。
若是兩個線程引用了兩個單獨的Counter實例,那麼同時調用add()方法就沒有問題。 調用將是對不一樣的對象,所以調用的方法也將在不一樣的對象(擁有該方法的對象)上同步。 所以呼叫不會阻止。 這是如何看起來:
public class Example { public static void main(String[] args) { Counter counterA = new Counter(); Counter counterB = new Counter(); Thread threadA = new CounterThread(counterA); Thread threadB = new CounterThread(counterB); threadA.start(); threadB.start(); } }
注意兩個線程threadA和threadB如何再也不引用相同的計數器實例。 counterA和counterB的add方法在它們的兩個擁有實例上同步。 所以,在counterA上調用add()將不會阻止對counterB的add()調用。
同步機制是Java的第一種機制,用於同步對多個線程共享的對象的訪問。但同步機制並非很先進。 這就是爲何Java 5得到了一整套concurrency utility classes來幫助開發人員實現比同步所得到的更細粒度的併發控制。
這篇文章對synchronized關鍵字進行了詳細的講解,我的感受講解的不錯,翻譯出來,作個記錄。
http://tutorials.jenkov.com/java-concurrency/synchronized.html