搜尋
首頁Javajava教程Java重寫鎖的設計結構和細節是什麼

    引導語

    有的面試官喜歡讓同學在說完鎖的原理之後,讓你重寫一個新的鎖,要求現場在白板上寫出大概的思路和程式碼邏輯,這種面試題目,蠻難的,我個人覺得其重點主要是兩個部分:

    考察一下你對鎖原理的理解是如何來的,如果你對源碼沒有解讀過的話,只是看看網上的文章,或者背面試題,也是能夠說出大概的原理,但你很難現場寫出一個鎖的實現代碼,除非你真的看過源碼,或有和鎖相關的專案經驗;

    我們不需要創造,我們只需要模仿Java 鎖定中現有的API 進行重寫即可。

    如果你看過原始碼,這題真的很簡單,你可以挑選一個你熟悉的鎖來模仿。

    1、需求

    一般自訂鎖的時候,我們都是根據需求來進行定義的,不可能憑空定義出鎖來,說到共享鎖,大家可能會想到很多場景,比如說對於共享資源的讀鎖可以是共享的,例如對於資料庫連結的共享訪問,例如對於Socket 服務端的連結數是可以共享的,場景有很多,我們選擇共享訪問資料庫連結這個場景來定義一個鎖。

    2、詳細設計

    假定(以下設想都為假定)我們的資料庫是單機mysql,只能承受10 個鏈接,在建立資料庫連結時,我們是透過最原始JDBC 的方式,我們用一個介面把用JDBC 建立連結的過程進行了封裝,這個介面我們命名為:建立連結介面。

    共享存取資料庫連結的整體要求如下:所有請求加在一起的 mysql 連結數,最大不能超過 10(包含 10),一旦超過 10,直接報錯。

    在這個背景下,我們進行了下圖的設計:

    Java重寫鎖的設計結構和細節是什麼

    這個設計最關鍵的地方,就是我們透過能否獲得鎖,來決定是否可以獲得mysql 鏈接,如果能獲得鎖,那麼就能得到鏈接,否則直接報錯。

    接著我們一起來看下落地的程式碼:

    2.1、定義鎖定 

    首先我們需要定義一個鎖出來,定義時需要有兩個元素:

    鎖的定義:同步器Sync;鎖對外提供的加鎖和解鎖的方法。

    共享鎖定的程式碼實作如下:

    // 共享不公平锁
    public class ShareLock implements Serializable{
    	// 同步器
      private final Sync sync;
      // 用于确保不能超过最大值
      private final int maxCount;
      /**
       * 初始化时给同步器 sync 赋值
       * count 代表可以获得共享锁的最大值
       */
      public ShareLock(int count) {
        this.sync = new Sync(count);
        maxCount = count;
      }
      /**
       * 获得锁
       * @return true 表示成功获得锁,false 表示失败
       */
      public boolean lock(){
        return sync.acquireByShared(1);
      }
      /**
       * 释放锁
       * @return true 表示成功释放锁,false 表示失败
       */
      public boolean unLock(){
        return sync.releaseShared(1);
      }
    }

    從上述程式碼可以看出,加上鎖定和釋放鎖定的實現,都依賴同步器 Sync 的底層實作。

    唯一要注意的是,鎖定需要規定好API 的規範,主要是兩方面:

    API 需要什麼,就是鎖在初始化的時候,你需要傳哪些參數給我,在ShareLock 初始化時,需要傳最大可共享鎖定的數量;

    #需要定義自身的能力,也就是定義每個方法的入參和出參。在ShareLock 的實作中,加鎖和釋放鎖的入參都沒有,是方法裡面寫死的1,表示每次方法執行,只能加鎖一次或釋放鎖一次,出參是布林值,true 表示加鎖或釋放鎖成功,false 表示失敗,底層使用的都是Sync 非公平鎖。

    以上這種思考方式是有方法論的,就是我們在思考一個問題時,可以從兩個面向出發:API 是什麼? API 有什麼能力?

    2.2、定義同步器Sync

    Sync 直接繼承AQS ,程式碼如下:

    class Sync extends AbstractQueuedSynchronizer {
       // 表示最多有 count 个共享锁可以获得
      public Sync(int count) {
        setState(count);
      }
      // 获得 i 个锁
      public boolean acquireByShared(int i) {
        // 自旋保证 CAS 一定可以成功
        for(;;){
          if(i<=0){
            return false;
          }
          int state = getState();
          // 如果没有锁可以获得,直接返回 false
          if(state <=0 ){
            return false;
          }
          int expectState = state - i;
          // 如果要得到的锁不够了,直接返回 false
          if(expectState < 0 ){
            return false;
          }
          // CAS 尝试得到锁,CAS 成功获得锁,失败继续 for 循环
          if(compareAndSetState(state,expectState)){
            return true;
          }
        }
      }
      // 释放 i 个锁
      @Override
      protected boolean tryReleaseShared(int arg) {
        for(;;){
          if(arg<=0){
            return false;
          }
          int state = getState();
          int expectState = state + arg;
          // 超过了 int 的最大值,或者 expectState 超过了我们的最大预期
          if(expectState < 0 || expectState > maxCount){
            log.error("state 超过预期,当前 state is {},计算出的 state is {}",state
            ,expectState);
            return false;
          }
          if(compareAndSetState(state, expectState)){
            return true;
          }
        }
      }
    }

    整個程式碼比較清晰,我們要注意的是:

    邊界的判斷,例如入參是否非法,釋放鎖時,會不會出現預期的state 非法等邊界問題,對於此類問題我們都需要加以判斷,體現出思維的嚴謹性;

    加鎖和釋放鎖,需要用for 自旋CAS 的形式,來確保當並發加鎖或釋放鎖時,可以重試成功。寫 for 自旋時,我們需要注意在適當的時機要 return,不要造成死循環,CAS 的方法 AQS 已經提供了,不要自己寫,我們自己寫的 CAS 方法是無法保證原子性的。

    2.3、透過能否獲得鎖來決定能否得到連結

    鎖定定義好了,我們需要把鎖和取得Mysql 連結結合起來,我們寫了一個Mysql 連結的工具類,稱為MysqlConnection,其主要負責兩大功能:

    透過JDBC 建立和Mysql 的連結;

    結合鎖,來防止請求過大時,Mysql 的總連結數不能超過10 個。

    首先我們看下MysqlConnection 初始化的程式碼:

    public class MysqlConnection {
      private final ShareLock lock;
      // maxConnectionSize 表示最大链接数
      public MysqlConnection(int maxConnectionSize) {
        lock = new ShareLock(maxConnectionSize);
      }
    }

    我們可以看到,在初始化時,需要製定最大的連結數是多少,然後把這個數值傳遞給鎖,因為最大的連結數就是ShareLock 鎖的state 值。

    接著為了完成 1,我們寫了一個 private 的方法:

    // 得到一个 mysql 链接,底层实现省略
    private Connection getConnection(){}

    然後我們實作 2,程式碼如下:

    // 对外获取 mysql 链接的接口
    // 这里不用try finally 的结构,获得锁实现底层不会有异常
    // 即使出现未知异常,也无需释放锁
    public Connection getLimitConnection() {
      if (lock.lock()) {
        return getConnection();
      }
      return null;
    }
    // 对外释放 mysql 链接的接口
    public boolean releaseLimitConnection() {
      return lock.unLock();
    }

    逻辑也比较简单,加锁时,如果获得了锁,就能返回 Mysql 的链接,释放锁时,在链接关闭成功之后,调用 releaseLimitConnection 方法即可,此方法会把锁的 state 状态加一,表示链接被释放了。

    以上步骤,针对 Mysql 链接限制的场景锁就完成了。

    3、测试

    锁写好了,接着我们来测试一下,我们写了一个测试的 demo,代码如下:

    public static void main(String[] args) {
      log.info("模仿开始获得 mysql 链接");
      MysqlConnection mysqlConnection = new MysqlConnection(10);
      log.info("初始化 Mysql 链接最大只能获取 10 个");
      for(int i =0 ;i<12;i++){
        if(null != mysqlConnection.getLimitConnection()){
          log.info("获得第{}个数据库链接成功",i+1);
        }else {
          log.info("获得第{}个数据库链接失败:数据库连接池已满",i+1);
        }
      }
      log.info("模仿开始释放 mysql 链接");
      for(int i =0 ;i<12;i++){
        if(mysqlConnection.releaseLimitConnection()){
          log.info("释放第{}个数据库链接成功",i+1);
        }else {
          log.info("释放第{}个数据库链接失败",i+1);
        }
      }
      log.info("模仿结束");
    }

    以上代码逻辑如下:

    获得 Mysql 链接逻辑:for 循环获取链接,1~10 都可以获得链接,11~12 获取不到链接,因为链接被用完了;释放锁逻辑:for 循环释放链接,1~10 都可以释放成功,11~12 释放失败。

    我们看下运行结果,如下图:

    Java重寫鎖的設計結構和細節是什麼

    从运行的结果,可以看出,我们实现的 ShareLock 锁已经完成了 Mysql 链接共享的场景了。

    以上是Java重寫鎖的設計結構和細節是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述
    本文轉載於:亿速云。如有侵權,請聯絡admin@php.cn刪除
    是否有任何威脅或增強Java平台獨立性的新興技術?是否有任何威脅或增強Java平台獨立性的新興技術?Apr 24, 2025 am 12:11 AM

    新興技術對Java的平台獨立性既有威脅也有增強。 1)雲計算和容器化技術如Docker增強了Java的平台獨立性,但需要優化以適應不同雲環境。 2)WebAssembly通過GraalVM編譯Java代碼,擴展了其平台獨立性,但需與其他語言競爭性能。

    JVM的實現是什麼,它們都提供了相同的平台獨立性?JVM的實現是什麼,它們都提供了相同的平台獨立性?Apr 24, 2025 am 12:10 AM

    不同JVM實現都能提供平台獨立性,但表現略有不同。 1.OracleHotSpot和OpenJDKJVM在平台獨立性上表現相似,但OpenJDK可能需額外配置。 2.IBMJ9JVM在特定操作系統上表現優化。 3.GraalVM支持多語言,需額外配置。 4.AzulZingJVM需特定平台調整。

    平台獨立性如何降低發展成本和時間?平台獨立性如何降低發展成本和時間?Apr 24, 2025 am 12:08 AM

    平台獨立性通過在多種操作系統上運行同一套代碼,降低開發成本和縮短開發時間。具體表現為:1.減少開發時間,只需維護一套代碼;2.降低維護成本,統一測試流程;3.快速迭代和團隊協作,簡化部署過程。

    Java的平台獨立性如何促進代碼重用?Java的平台獨立性如何促進代碼重用?Apr 24, 2025 am 12:05 AM

    Java'splatformindependencefacilitatescodereusebyallowingbytecodetorunonanyplatformwithaJVM.1)Developerscanwritecodeonceforconsistentbehavioracrossplatforms.2)Maintenanceisreducedascodedoesn'tneedrewriting.3)Librariesandframeworkscanbesharedacrossproj

    您如何在Java應用程序中對平台特定問題進行故障排除?您如何在Java應用程序中對平台特定問題進行故障排除?Apr 24, 2025 am 12:04 AM

    要解決Java應用程序中的平台特定問題,可以採取以下步驟:1.使用Java的System類查看系統屬性以了解運行環境。 2.利用File類或java.nio.file包處理文件路徑。 3.根據操作系統條件加載本地庫。 4.使用VisualVM或JProfiler優化跨平台性能。 5.通過Docker容器化確保測試環境與生產環境一致。 6.利用GitHubActions在多個平台上進行自動化測試。這些方法有助於有效地解決Java應用程序中的平台特定問題。

    JVM中的類加載程序子系統如何促進平台獨立性?JVM中的類加載程序子系統如何促進平台獨立性?Apr 23, 2025 am 12:14 AM

    類加載器通過統一的類文件格式、動態加載、雙親委派模型和平台無關的字節碼,確保Java程序在不同平台上的一致性和兼容性,實現平台獨立性。

    Java編譯器會產生特定於平台的代碼嗎?解釋。Java編譯器會產生特定於平台的代碼嗎?解釋。Apr 23, 2025 am 12:09 AM

    Java編譯器生成的代碼是平台無關的,但最終執行的代碼是平台特定的。 1.Java源代碼編譯成平台無關的字節碼。 2.JVM將字節碼轉換為特定平台的機器碼,確保跨平台運行但性能可能不同。

    JVM如何處理不同操作系統的多線程?JVM如何處理不同操作系統的多線程?Apr 23, 2025 am 12:07 AM

    多線程在現代編程中重要,因為它能提高程序的響應性和資源利用率,並處理複雜的並發任務。 JVM通過線程映射、調度機制和同步鎖機制,在不同操作系統上確保多線程的一致性和高效性。

    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脫衣器

    Video Face Swap

    Video Face Swap

    使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

    熱工具

    MantisBT

    MantisBT

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

    EditPlus 中文破解版

    EditPlus 中文破解版

    體積小,語法高亮,不支援程式碼提示功能

    ZendStudio 13.5.1 Mac

    ZendStudio 13.5.1 Mac

    強大的PHP整合開發環境

    Safe Exam Browser

    Safe Exam Browser

    Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

    SublimeText3 Mac版

    SublimeText3 Mac版

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