Java:synchronized關鍵字
原帖收藏於IT老兵部落格。
前言
這裡翻譯一篇對Java的synchronized關鍵字的講解文章,這個關鍵字用於解決Java世界裡面競爭條件時的訪問衝突問題。
正文
Java synchronized塊將方法或程式碼塊標記為已同步。 Java synchronized塊可用於避免競爭條件。
Java同步關鍵字
Java中的同步塊使用synchronized關鍵字標記。Java中的同步塊在某個物件上同步。在同一物件上的所有同步塊在同一時刻只能被一個執行緒執行。嘗試進入同步塊的所有其他執行緒將被阻塞,直到同步塊內的執行緒退出塊。
synchronized關鍵字可用於標記四種不同型別的塊:
- 例項方法
- 靜態方法
- 例項方法中的程式碼塊
- 靜態方法中的程式碼塊
這些塊在不同物件上同步,您需要哪種型別的同步塊取決於具體情況。
同步例項方法
這是一個同步的例項方法:
public synchronized void add(int value){
this.count + = value;
}
請注意在方法宣告中使用synchronized關鍵字,這告訴Java該方法是同步的。
Java中的同步例項方法在擁有該方法的例項(物件)上同步。因此,每個例項的同步方法在不同的物件上同步:擁有它的例項。在一個同步例項方法中,只有一個執行緒可以執行。如果存在多個例項,則一次只能有一個執行緒可以在每個例項的同步例項方法內執行。每個例項一個執行緒。
同步靜態方法
靜態方法被標記為synchronized,就像使用synchronized關鍵字的例項方法一樣。這是一個Java synchronized靜態方法示例:
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不同的物件上同步,那麼一個執行緒可以同時在每個方法內執行。
Java同步例子
下面是一個示例,它啟動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的第一種機制,用於同步對多個執行緒共享的物件的訪問。但同步機制並不是很先進。 這就是為什麼Java 5獲得了一整套concurrency utility classes來幫助開發人員實現比同步所獲得的更細粒度的併發控制。
總結
這篇文章對synchronized關鍵字進行了詳細的講解,個人感覺講解的不錯,翻譯出來,做個記錄。
參考
http://tutorials.jenkov.com/java-concurrency/synchronized.html