目次
はじめに
1. 要件
2. 詳細な設計
2.1. ロックの定義
2.2. シンクロナイザーの定義 Sync
2.3. ロックが取得できるかどうかによって、リンクが取得できるかどうかを判断します。
3、测试
ホームページ Java &#&チュートリアル Javaリライトロックの設計構造と詳細は何ですか

Javaリライトロックの設計構造と詳細は何ですか

Apr 18, 2023 pm 05:22 PM
java

    はじめに

    一部の面接官は、学生にロックの原理を説明した後に新しいロックを書き直すよう求め、現場に立ち会ってもらうことを好みます。一般的なアイデアとコード ロジックをホワイトボードに書きます。この種の面接の質問は非常に難しいです。個人的には、次の 2 つの部分に焦点が当てられていると思います:

    ロック原理をどのように理解したか調べてください。まだ理解していない場合は、ソースコードを解釈すれば、オンラインの記事を読んだり、インタビューの質問を覚えたりするだけで大​​まかな原理はわかりますが、実際にソースコードを読んでいない限り、その場でロックの実装コードを書くのは困難です。ロックに関連するプロジェクトの経験がある;

    作成する必要はなく、Java ロックの既存の API を模倣して書き直すだけで済みます。

    ソース コードを読んだことがあれば、この質問は非常に簡単です。使い慣れたロックを選択して模倣することができます。

    1. 要件

    一般に、ロックをカスタマイズするときは、要件に基づいてロックを定義します。何もないところからロックを定義することは不可能です。共有ロックに関しては、多くのものが思い浮かぶかもしれません。シナリオ、たとえば、共有リソースの読み取りロックを共有できる、データベース リンクへの共有アクセスなど、たとえば、ソケット サーバー上のリンクの数を共有できるなど、多くのシナリオがあります。データベース リンクにアクセスしてロックを定義します。

    2. 詳細な設計

    データベースはスタンドアロンの mysql であり、10 個の接続しかサポートできないと仮定します (以下の前提はすべて前提です)。データベース接続を作成するときは、次を使用します。オリジナルの JDBC このように、JDBC を使用してリンクを作成するプロセスをカプセル化するインターフェイスを使用します。このインターフェイスに Create Link Interface という名前を付けます。

    共有アクセス データベース リンクの全体的な要件は次のとおりです: すべてのリクエストを合わせた mysql リンクの数は 10 (両端を含む) を超えることはできません。10 を超えると、エラーが直接報告されます。

    これに関連して、以下に示すように設計を実行しました。

    Javaリライトロックの設計構造と詳細は何ですか

    この設計の最も重要な部分は、それを介してロックを取得できるかどうかです。 mysql リンクを取得できるかどうかを判断するには、ロックを取得できる場合はリンクを取得でき、そうでない場合はエラーが直接報告されます。

    次に、実装されたコードを見てみましょう:

    2.1. ロックの定義

    最初にロックを定義する必要があり、それには 2 つの要素が必要です:

    ロックの定義: シンクロナイザー 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 の実装に依存しています。

    注意する必要がある唯一のことは、ロックには主に 2 つの側面で優れた API 仕様が必要であるということです。

    API で必要なのは、次のときにどのようなパラメーターを渡す必要があるかです。 ShareLock を初期化するときは、共有可能なロックの最大数を渡す必要があります。

    独自の機能を定義する必要があります。つまり、各メソッドの入力パラメータと出力パラメータを定義する必要があります。 ShareLock の実装には、ロックのロックと解放のための入力パラメータはありません。これらはメソッド内でハードコーディングされています 1。つまり、メソッドが実行されるたびに、ロックは 1 回のみロックまたは解放されます。出力パラメータはブール値で、true はロックまたはロックの解放が成功したことを意味し、false は失敗を示し、最下位層は不当な同期ロックを使用します。

    上記の考え方には方法論があります。つまり、問題を考えるとき、次の 2 つの側面から始めることができます。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;
          }
        }
      }
    }
    ログイン後にコピー

    コード全体は比較的明確ですが、注意する必要がある点は次のとおりです。

    入力パラメータが不正かどうか、ロックを解除したときに予想される不正な状態が発生するかどうかなどの境界判断、思考の厳密さを反映して判断する必要がある;

    追加 ロックを同時にロックまたは解放するときに再試行が成功することを保証するには、ロックとロックの解放をスピン CAS の形式にする必要があります。スピン用に書く場合は、適切なタイミングでリターンすることと、無限ループにならないように注意する必要があります。CAS メソッドは AQS から提供されています。自分で書かないでください。自分で書いた CAS メソッドはアトミック性を保証できません。

    2.3. ロックが取得できるかどうかによって、リンクが取得できるかどうかを判断します。

    ロックを定義した後、ロックと Mysql リンクの取得を組み合わせる必要があります。 MysqlConnection と呼ばれる Mysql リンク ツール クラスは、主に 2 つの主要な機能を担当します:

    JDBC を介した Mysql とのリンクの確立;

    Mysql リンクの総数が超過しないようにロックと組み合わせる10 リクエストが大きすぎる場合。

    まず、MysqlConnection 初期化コードを見てみましょう:

    public class MysqlConnection {
      private final ShareLock lock;
      // maxConnectionSize 表示最大链接数
      public MysqlConnection(int maxConnectionSize) {
        lock = new ShareLock(maxConnectionSize);
      }
    }
    ログイン後にコピー

    初期化中に、リンクの最大数を指定し、この値をロックに渡す必要があることがわかります。リンクの最大数が ShareLock ロックの状態値であるためです。

    次に、1 を完了するために、プライベート メソッドを作成しました:

    // 得到一个 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 中国語 Web サイトの他の関連記事を参照してください。

    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

    リアルなヌード写真を作成する AI 搭載アプリ

    AI Clothes Remover

    AI Clothes Remover

    写真から衣服を削除するオンライン AI ツール。

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    Video Face Swap

    Video Face Swap

    完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

    ホットツール

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    SublimeText3 中国語版

    SublimeText3 中国語版

    中国語版、とても使いやすい

    ゼンドスタジオ 13.0.1

    ゼンドスタジオ 13.0.1

    強力な PHP 統合開発環境

    ドリームウィーバー CS6

    ドリームウィーバー CS6

    ビジュアル Web 開発ツール

    SublimeText3 Mac版

    SublimeText3 Mac版

    神レベルのコード編集ソフト(SublimeText3)

    Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

    Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

    ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

    Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

    Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

    Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

    Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

    この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

    Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

    Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

    Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

    Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

    カプセルの量を見つけるためのJavaプログラム カプセルの量を見つけるためのJavaプログラム Feb 07, 2025 am 11:37 AM

    カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

    未来を創る: まったくの初心者のための Java プログラミング 未来を創る: まったくの初心者のための Java プログラミング Oct 13, 2024 pm 01:32 PM

    Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。

    See all articles