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

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

Jan 05, 2017 pm 03:48 PM

多執行緒程式設計中,最關鍵、最關心的問題應該就是同步問題,這是一個難點,也是核心。
從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

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java的類負載機制如何起作用,包括不同的類載荷及其委託模型? Java的類負載機制如何起作用,包括不同的類載荷及其委託模型? Mar 17, 2025 pm 05:35 PM

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存? 如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存? Mar 17, 2025 pm 05:44 PM

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射? 如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射? Mar 17, 2025 pm 05:43 PM

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案? 如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案? Mar 17, 2025 pm 05:46 PM

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)? 如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)? Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

See all articles