Java:synchronized關鍵字

原帖收藏於IT老兵博客html

前言

這裏翻譯一篇對Java的synchronized關鍵字的講解文章,這個關鍵字用於解決Java世界裏面競爭條件時的訪問衝突問題。java

正文

Java synchronized塊將方法或代碼塊標記爲已同步。 Java synchronized塊可用於避免競爭條件。web

Java同步關鍵字

Java中的同步塊使用synchronized關鍵字標記。Java中的同步塊在某個對象上同步。在同一對象上的全部同步塊在同一時刻只能被一個線程執行。嘗試進入同步塊的全部其餘線程將被阻塞,直到同步塊內的線程退出塊。併發

synchronized關鍵字可用於標記四種不一樣類型的塊:svg

  1. 實例方法
  2. 靜態方法
  3. 實例方法中的代碼塊
  4. 靜態方法中的代碼塊

這些塊在不一樣對象上同步,您須要哪一種類型的同步塊取決於具體狀況。函數

同步實例方法

這是一個同步的實例方法: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不一樣的對象上同步,那麼一個線程能夠同時在每一個方法內執行。

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

相關文章
相關標籤/搜索