首頁  >  文章  >  Java  >  Java同步程式碼區塊的詳細實例程式碼介紹

Java同步程式碼區塊的詳細實例程式碼介紹

黄舟
黄舟原創
2017-02-28 10:47:182216瀏覽

一個Java同步區塊使得一個方法或程式碼區塊作為同步的。 Java同步區塊可以用來避免靜態條件。

這個Java同步關鍵字

在Java中的同步區塊透過使用synchronized關鍵字作為標記。在Java中的一個同步區塊是在一些物件上同步的。同步在所有物件上的所有同步區塊只能同時有一個執行緒執行。所有嘗試進入到同步區塊的其他執行緒被堵塞,直到同步區塊內部的這個執行緒離開這個區塊。

這個synchronized關鍵字可以用來標記四種不同類型的區塊:


  1. 實例方法

  2. 靜態方法

  3. 實例方法中的程式碼區塊

  4. #靜態方法中的程式碼區塊

#這些區塊在不同的物件中被同步。你需要哪種同步區塊類型依賴特定的場景。

同步的實例方法

這裡有一個例子:

  public synchronized void add(int value){
      this.count += value;
  }

注意在方法生命中synchronized關鍵字的使用。這個告訴Java這個方法被同步了。

在Java中的一個同步實例方法是在屬於這個方法的實例物件上同步的。因此,每一個實例都有它的同步方法同步在不同的物件上:自己擁有的實例。只有一個執行緒可以執行一個實例同步方法,然後一個執行緒可以每次執行一個同步實例方法。每一個實例一個執行緒。

同步的靜態方法

靜態方法被標記為同步的就跟實例方法使用synchronized關鍵字一樣。這裡有一個實例:

  public static synchronized void add(int value){
      count += value;
  }

這裡也有一個synchronized關鍵字告訴Java這個方法是同步的。

同步的靜態方法被同步在靜態方法屬於的這個類別的類別物件上。因為在Java虛擬機器中每個類別只有一個類別物件存在,只有一個執行緒可以執行在相同類別的同步靜態方法中。

如果這個靜態同步方法位於不同的類別中,然後一個執行緒可以執行每個類別的內部的靜態同步方法。每一個類別的執行緒不管他們呼叫的哪個靜態同步方法。

在實例方法中的同步區塊

你不能同步整個方法。有的時候同步一個方法的部分是比較好。方法內部的Java同步區塊使得這個是可能的。

這裡有一個例子:


  public void add(int value){

    synchronized(this){
       this.count += value;   
    }
  }

這個例子使用Java同步區塊去建立一個程式碼區塊作為同步的。如果它是一個同步的方法,這個方法將會執行的。

注意,這個Java同步區塊是怎麼樣帶著一個物件的部分建構的。在這個例子中,「this」被使用了,這個就是被呼叫的add方法的這個實例。透過同步構造的接受括號中的這個物件稱之為監視器物件。這個程式碼在監視器物件上就認為將會是同步的。一個同步的實例方法使用它所屬於的這個物件作為監視物件。

只有一個執行緒可以執行在相同監視物件的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);
       }
    }
  }


因此只有一個執行緒可以執行這個範例中的兩個同步區塊中的其中一個。

有第二個同步區塊被同步在不同的物件上對this來說,然後一個執行緒一次只能執行內部的一個方法。

在靜態方法中的同步區塊

這裡有兩個相同的實例作為靜態方法。這些方法被同步在這個方法屬於的這個類別的類別物件中:

  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同步實例

這裡有一個實例,啟動兩個線程,他們都去呼叫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實例傳遞給了它們的建構子。這個add方法在實例上被同步了,因為這個add方法是一個實例方法,並且標記為同步的。因此一次只能有一個執行緒呼叫這個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(); 
    }
  }

注意这两个线程,他们不再引用相同的实例。counterA和counterB的add方法同步在它们自己的实例上。因此不会堵塞。

Java并发工具

这个synchronized机制是java的第一个途径对于访问同步访问被多线程共享的对象。但是这个synchronized机制不是最高级的。那就是为什么Java 5提供了一个并发工具类的集合去帮助开发者实现更细粒度的并发控制相对于synchronized而言。


 以上就是Java同步代码块的详细实例代码介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn