首頁 >Java >java教程 >Java多執行緒程式設計中synchronized關鍵字的基礎用法講解

Java多執行緒程式設計中synchronized關鍵字的基礎用法講解

高洛峰
高洛峰原創
2017-01-05 15:48:151510瀏覽

多執行緒程式設計中,最關鍵、最關心的問題應該就是同步問題,這是一個難點,也是核心。
從jdk最早的版本的synchronized、volatile,到jdk 1.5中提供的java.util.concurrent.locks套件中的Lock介面(實作有ReadLock,WriteLock,ReentrantLock),多執行緒的實作也是一步步走向成熟化。
 
同步,它是透過什麼機制來控制的呢?第一反應就是鎖,這個在學習作業系統與資料庫的時候,應該都已經接觸到了。在Java的多執行緒程式中,當多個程式競爭同一個資源時,為了防止資源的腐蝕,給第一個存取資源的執行緒分配一個物件鎖,而後來者需要等待這個物件鎖的釋放。
 
是的,Java執行緒的同步,最關心的是共享資源的使用。
 
先來了解一些有哪些執行緒的共享資源,
從JVM了解有哪些執行緒共享的資料是需要進行協調:
1,保存在堆中的實例變數;2,保存在方法區的類別變數。
 
而在Java虛擬機器載入類別的時候,每個物件或類別都會與一個監視器相關聯,用來保護物件的實例變數或類別變數;當然,如果物件沒有實例變量,或類別沒有變量,監視器就什麼也不監視了。
 
為了實現上面的說的監視器的互斥性,虛擬機為每一個物件或類別都關聯了一個鎖(也叫隱形鎖),這裡說明一下,類別鎖也是透過物件鎖來實現的,因為在類別載入的時候,JVM會為每個類別建立一個java.lang.Class的一個實例;所以當鎖定對物件的時候,也就鎖住這個類別的類別物件。
 
另外,一個執行緒是可以對一個物件進行多次上鎖,也就對應著多次釋放;它是透過JVM為每個物件鎖提供的lock計算器,上一次鎖,就加1,對應的減1,當計算機的值為0時,就釋放。這個物件鎖是JVM內部的監視器使用的,也是由JVM自動產生的,所有程式猿就不用自己動手來加了。
 
介紹完java的同步原理後,我們進入正題,先來說說synchronized的使用,而其它的同步,將在後面的章節中介紹。
 
先來運行一個範例試試。

package thread_test; 
  
/** 
 * 测试扩展Thread类实现的多线程程序 
 * 
 */
public class TestThread extends Thread{  
  private int threadnum; 
  
  public TestThread(int threadnum) {  
    this.threadnum = threadnum;  
  } 
    
  @Override
  public synchronized void run() {  
    for(int i = 0;i<1000;i++){  
          System.out.println("NO." + threadnum + ":" + i ); 
    } 
    }  
    
    public static void main(String[] args) throws Exception {  
      for(int i=0; i<10; i++){ 
          new TestThread(i).start(); 
          Thread.sleep(1); 
      } 
    }  
}

運行結果:

NO.0:887 
NO.0:888 
NO.0:889 
NO.0:890 
NO.0:891 
NO.0:892 
NO.0:893 
NO.0:894 
NO.7:122 
NO.7:123 
NO.7:124

上面只是一個片段,說明一個問題而已。
細心的童鞋會發現,NO.0:894後面是NO.7:122,也就是說沒有按照從0開始到999。
都說synchronized可以實作同步方法或同步區塊,這裡怎麼就不行?
 
先從同步的機制來分析一下,同步是透過鎖來實現的,那麼上面的例子中,鎖定了什麼對象,或鎖定了什麼類別呢?裡面有兩個變量,一個是i,一個是threadnum;i是方法內部的,threadnum是私有的。
再來了解synchronized的運作機制:
      在java程式中,當使用synchronized區塊或synchronized方法時,標誌這個區域進行監視;而JVM在處理程序時,當有程式進入監視區域時,就會自動鎖上對像或類別。
 
那麼上面的例子中,synchronized關鍵字用上後,鎖定的是什麼呢?
當synchronized方法時,鎖定呼叫方法的實例物件本身會做為物件鎖定。本例中,10個執行緒都有自己建立的TestThread的類別對象,所以取得的對象鎖,也是自己的對象鎖,與其它執行緒沒有任何關係。
 
要實現方法鎖定,必須鎖定有共享的物件。
 
對上面的實例修改一下,再看看:

package thread_test; 
  
/** 
 * 测试扩展Thread类实现的多线程程序 
 * 
 */
public class TestThread extends Thread{  
  private int threadnum; 
  private String flag;  //标记 
    
  public TestThread(int threadnum,String flag) {  
       this.threadnum = threadnum;  
        this.flag = flag; 
    } 
    
  @Override
    public void run() {  
    synchronized(flag){ 
      for(int i = 0;i<1000;i++){  
              System.out.println("NO." + threadnum + ":" + i ); 
          }  
    } 
    }  
  
    public static void main(String[] args) throws Exception {  
      String flag = new String("flag"); 
      for(int i=0; i<10; i++){ 
          new TestThread(i,flag).start(); 
          Thread.sleep(1); 
      } 
    }  
}

也就加了一個共享的標誌flag。然後在通過synchronized區塊,對flag標誌進行同步;這就滿足了鎖定共享物件的條件。
是的,運行結果,已經按順序來了。

透過synchronized區塊,指定取得物件鎖定來達到同步的目的。那有沒有其它的方法,可以透過synchronized方法來實現呢?
 
依據同步的原理:若能取得共享物件鎖或類別鎖,可實現同步。那我們是不是可以透過共享一個類別鎖來實現呢?
 
是的,我們可以使用靜態同步方法,根據靜態方法的特性,它只允許類別物件本身才可以調用,不能透過實例化一個類別物件來調用。那麼如果得到了這個靜態方法的鎖,也就是得到這個類別鎖,而這個類別鎖都是TestThread類別鎖,及達到了取得共享類別鎖的目的。
 
實作程式碼如下:

package thread_test; 
  
/** 
 * 测试扩展Thread类实现的多线程程序 
 * 
 * @author ciding 
 * @createTime Dec 7, 2011 9:37:25 AM 
 * 
 */
public class TestThread extends Thread{  
  private int threadnum; 
    
  public TestThread(int threadnum) {  
    this.threadnum = threadnum;  
  } 
    
  public static synchronized void staticTest(int threadnum) {  
    for(int i = 0;i<1000;i++){  
      System.out.println("NO." + threadnum + ":" + i ); 
    }  
  }  
  
  public static void main(String[] args) throws Exception {  
    for(int i=0; i<10; i++){ 
      new TestThread(i).start(); 
      Thread.sleep(1); 
    } 
  }  
    
  @Override
  public void run(){ 
    staticTest(threadnum); 
  } 
}

 運作結果略,與第二個範例中相同。
 
 
以上的內容主要是說明兩個問題:同步區塊與同步方法。
1,同步區塊:取得的物件鎖是synchronized(flag)中的flag物件鎖。
2,同步方法:取得的是方法所屬的類別對象,及類別物件鎖定。
靜態同步方法,由於多個執行緒都會共享,所以一定會同步。
而非靜態同步方法,只有在單例模式下才會同步。


更多Java多執行緒程式設計中synchronized關鍵字的基礎用法講解相關文章請關注PHP中文網!


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