搜尋
首頁Javajava教程Java 並發程式設計學習筆記之Synchronized簡介

一、Synchronized的基本使用

  Synchronized是Java中解決並發問題的一種最常用的方法,也是最簡單的一種方法。 Synchronized的功能主要有三:(1)確保執行緒互斥的存取同步程式碼(2)確保共享變數的修改能夠及時可見(3)有效解決重排序問題。從語法上講,Synchronized總共有三種用法:

  (1)修飾普通方法

  (2)修飾靜態方法

  (3)修飾代碼塊🎀

〜接下來幾個例子三種使用方式(為了方便比較,三段程式碼除了Synchronized的使用方式不同以外,其他基本上保持一致)。

1、沒有同步的情況:

程式碼段一:

package com.paddx.test.concurrent;
 
public class SynchronizedTest {
  public void method1(){
    System.out.println("Method 1 start");
    try {
      System.out.println("Method 1 execute");
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Method 1 end");
  }
 
  public void method2(){
    System.out.println("Method 2 start");
    try {
      System.out.println("Method 2 execute");
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Method 2 end");
  }
 
  public static void main(String[] args) {
    final SynchronizedTest test = new SynchronizedTest();
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        test.method1();
      }
    }).start();
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        test.method2();
      }
    }).start();
  }
}

執行結果如下,執行緒1和執行緒2同時進入執行狀態,執行緒2執行速度比執行緒1快,所以執行緒2先執行完成,這個過程中線程1和線程2是同時執行的。

Method 1 start

Method 1 execute
Method 2 start
Method 2 execute
Method 2 end
Method 1 end

 2、對普通方法同步:以下程式碼同步段一比較,可以很明顯的看出,執行緒2需要等待執行緒1的method1執行完成才能開始執行method2方法。

Method 1 start

Method 1 execute

Method 1 end

Method 2 start

Method 2 execute

Method 2 end


3、靜態方法(類)同步化靜態方法的同步本質上是對類別的同步(靜態方法本質上是屬於類別的方法,而不是對像上的方法),所以即使test和test2屬於不同的對象,但是它們都屬於SynchronizedTest類別的實例,所以也只能順序的執行method1和method2,不能並發執行。

Method 1 start
Method 1 execute

Method 1 end

Method 2 start

Method 2 execute

Method 2 end

rr

4、代碼塊同步

Method 2 end

線程2都進入了對應的方法開始執行,但是線程2在進入同步區塊之前,需要等待線程1中同步區塊執行完成。

Method 1 start
Method 1 execute
Method 2 start

Method 1 end

Method 2 execute

Method 2 end

、Synchronized 問題還是急先來了解Synchronized的原理,再回頭上面的問題就一目了然了。我們先透過反編譯下面的程式碼來看看Synchronized是如何實現對程式碼區塊進行同步的:

package com.paddx.test.concurrent;
 
public class SynchronizedTest {
  public synchronized void method1(){
    System.out.println("Method 1 start");
    try {
      System.out.println("Method 1 execute");
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Method 1 end");
  }
 
  public synchronized void method2(){
    System.out.println("Method 2 start");
    try {
      System.out.println("Method 2 execute");
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Method 2 end");
  }
 
  public static void main(String[] args) {
    final SynchronizedTest test = new SynchronizedTest();
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        test.method1();
      }
    }).start();
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        test.method2();
      }
    }).start();
  }
}

反編譯結果:




關於這兩條指令的作用,我們直接參考JVM規範中描述:

monitorenter :

package com.paddx.test.concurrent;
  
 public class SynchronizedTest {
   public static synchronized void method1(){
     System.out.println("Method 1 start");
     try {
       System.out.println("Method 1 execute");
       Thread.sleep(3000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     System.out.println("Method 1 end");
   }
  
   public static synchronized void method2(){
     System.out.println("Method 2 start");
     try {
       System.out.println("Method 2 execute");
       Thread.sleep(1000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     System.out.println("Method 2 end");
   }
  
   public static void main(String[] args) {
     final SynchronizedTest test = new SynchronizedTest();
     final SynchronizedTest test2 = new SynchronizedTest();
  
     new Thread(new Runnable() {
       @Override
       public void run() {
         test.method1();
       }
     }).start();
  
     new Thread(new Runnable() {
       @Override
       public void run() {
         test2.method2();
       }
     }).start();
   }
 }

   

這段話的大概意思為:

Java 并发编程学习笔记之Synchronized简介每個物件都有監視器鎖定(monitor)。當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試取得monitor的所有權,過程如下:

1、如果monitor的進入數為0,則該線程進入monitor,然後將進入數設為1 ,該線程即為monitor的所有者。

2、如果線程已經佔有該monitor,只是重新進入,則進入monitor的進入數加1.

3.如果其他線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0 ,再重新嘗試取得monitor的所有權。

monitorexit: 

package com.paddx.test.concurrent;
 
public class SynchronizedTest {
  public void method1(){
    System.out.println("Method 1 start");
    try {
      synchronized (this) {
        System.out.println("Method 1 execute");
        Thread.sleep(3000);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Method 1 end");
  }
 
  public void method2(){
    System.out.println("Method 2 start");
    try {
      synchronized (this) {
        System.out.println("Method 2 execute");
        Thread.sleep(1000);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Method 2 end");
  }
 
  public static void main(String[] args) {
    final SynchronizedTest test = new SynchronizedTest();
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        test.method1();
      }
    }).start();
 
    new Thread(new Runnable() {
      @Override
      public void run() {
        test.method2();
      }
    }).start();
  }
}

   

這段話的大概意思是:

執行monitorexit的執行緒必須是objectref所對應的monitor的擁有者。

指令執行時,monitor的進入數減1,如果減1後進入數為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去取得這個 monitor 的所有權。

  透過這兩段描述,我們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是透過一個monitor的物件來完成,其實wait/notify等方法也依賴於monitor對象,這就是為什麼只有monitor在同步的區塊或方法中才能呼叫wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。

  我們再來看看同步方法的反編譯結果:

原始碼:

package com.paddx.test.concurrent;
 
public class SynchronizedMethod {
  public synchronized void method() {
    System.out.println("Hello World!");
  }
}

  从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

三、运行结果解释

  有了对Synchronized原理的认识,再来看上面的程序就可以迎刃而解了。

1、代码段2结果:

  虽然method1和method2是不同的方法,但是这两个方法都进行了同步,并且是通过同一个对象去调用的,所以调用之前都需要先去竞争同一个对象上的锁(monitor),也就只能互斥的获取到锁,因此,method1和method2只能顺序的执行。

2、代码段3结果:

  虽然test和test2属于不同对象,但是test和test2属于同一个类的不同实例,由于method1和method2都属于静态同步方法,所以调用的时候需要获取同一个类上monitor(每个类只对应一个class对象),所以也只能顺序的执行。

3、代码段4结果:

  对于代码块的同步实质上需要获取Synchronized关键字后面括号中对象的monitor,由于这段代码中括号的内容都是this,而method1和method2又是通过同一的对象去调用的,所以进入同步块之前需要去竞争同一个对象上的锁,因此只能顺序执行同步块。

四 总结

  Synchronized是Java并发编程中最常用的用于保证线程安全的方式,其使用相对也比较简单。但是如果能够深入了解其原理,对监视器锁等底层知识有所了解,一方面可以帮助我们正确的使用Synchronized关键字,另一方面也能够帮助我们更好的理解并发编程机制,有助我们在不同的情况下选择更优的并发策略来完成任务。对平时遇到的各种并发问题,也能够从容的应对。


更多Java 并发编程学习笔记之Synchronized简介相关文章请关注PHP中文网!


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
如何將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文件)的創建和使用。

如何使用咖啡因或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個字符]

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

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

See all articles

熱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.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),