Heim  >  Artikel  >  Java  >  Was sind die Designstruktur und Details der Java-Rewrite-Sperre?

Was sind die Designstruktur und Details der Java-Rewrite-Sperre?

王林
王林nach vorne
2023-04-18 17:22:031186Durchsuche

    Einführung

    Einige Interviewer bitten die Schüler gerne, ein neues Schloss neu zu schreiben, nachdem sie das Prinzip des Schlosses erklärt haben, und bitten Sie, die allgemeine Idee und die Codelogik auf das Whiteboard zu schreiben. Die Frage ist recht Ich persönlich denke, es konzentriert sich auf zwei Teile:

    Untersuchen Sie, wie Ihr Verständnis des Sperrprinzips zustande kommt. Wenn Sie den Quellcode nicht gelesen haben, lesen Sie einfach die Online-Artikel oder die Interviewfragen auf der Rückseite Allgemeines Prinzip, aber es ist schwierig für Sie, vor Ort einen Sperren-Implementierungscode zu schreiben, es sei denn, Sie haben den Quellcode wirklich gesehen oder Erfahrung mit Sperren-bezogenen Projekten

    Wir müssen nichts erstellen, wir machen es einfach Es muss lediglich neu geschrieben werden, um die vorhandene API in der Java-Sperre zu imitieren.

    Wenn Sie den Quellcode gelesen haben, ist diese Frage wirklich einfach. Sie können ein Ihnen bekanntes Schloss zum Nachahmen auswählen.

    1. Anforderungen

    Im Allgemeinen definieren wir Sperren basierend auf Anforderungen. Es ist unmöglich, Sperren aus dem Nichts zu definieren, beispielsweise für gemeinsam genutzte Ressourcen. Lesesperren können gemeinsam genutzt werden, beispielsweise kann die Anzahl der Links auf dem Socket-Server gemeinsam genutzt werden. Wir wählen das Szenario des gemeinsamen Zugriffs auf Datenbanklinks.

    2. Detailliertes Design

    Angenommen (die folgenden Annahmen sind alles Annahmen), dass unsere Datenbank eine eigenständige MySQL-Datenbank ist, die nur 10 Verbindungen unterstützen kann, verwenden wir die primitivste JDBC-Methode Verwenden Sie eine Schnittstelle. JDBC kapselt den Prozess der Linkerstellung. Wir nennen diese Schnittstelle: Link-Schnittstelle erstellen.

    Die allgemeinen Anforderungen für Datenbanklinks mit gemeinsamem Zugriff lauten wie folgt: Die Anzahl der MySQL-Links für alle Anforderungen zusammen darf 10 (einschließlich) nicht überschreiten. Sobald sie 10 überschreitet, wird direkt ein Fehler gemeldet.

    In diesem Zusammenhang haben wir das folgende Bild entworfen:

    Was sind die Designstruktur und Details der Java-Rewrite-Sperre?

    Der kritischste Teil dieses Entwurfs ist, dass wir entscheiden, ob wir den MySQL-Link erhalten können, indem wir die Sperre erhalten. dann Sie können den Link erhalten, andernfalls wird direkt ein Fehler gemeldet.

    Dann werfen wir einen Blick auf den implementierten Code:

    Definieren Sie die Sperre.

    Zuerst müssen wir eine Sperre definieren:

    Definition der Sperre: Synchronisierungsmethode; Sperre wird extern bereitgestellt und entsperren.

    Die Code-Implementierung der gemeinsamen Sperre lautet wie folgt:

    // 共享不公平锁
    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);
      }
    }

    Wie aus dem obigen Code ersichtlich ist, hängt die Implementierung des Sperrens und Freigebens von Sperren von der zugrunde liegenden Implementierung des Synchronisierers Sync ab.

    Das Einzige, worauf Sie achten müssen, ist, dass die Sperre die API-Spezifikationen angeben muss, hauptsächlich in zwei Aspekten:

    Was die API benötigt, ist, welche Parameter Sie mir übergeben müssen, wenn die Sperre initialisiert wird Wenn ShareLock initialisiert ist, müssen Sie die maximale Anzahl gemeinsam nutzbarer Sperren übergeben.

    Es müssen eigene Funktionen definiert werden, dh die Eingabe- und Ausgabeparameter jeder Methode. In der Implementierung von ShareLock gibt es keine Eingabeparameter zum Sperren und Freigeben der Sperre. Sie sind in der Methode fest codiert, was bedeutet, dass die Sperre bei jeder Ausführung der Methode nur einmal gesperrt oder freigegeben werden kann Der Ausgabeparameter ist ein boolescher Wert, und true bedeutet, dass das Hinzufügen oder Freigeben der Sperre erfolgreich ist, false bedeutet, dass ein Fehler aufgetreten ist, und die unterste Ebene verwendet eine unfaire Synchronisierungssperre.

    Die obige Denkweise hat eine Methodik, das heißt, wenn wir über ein Problem nachdenken, können wir von zwei Aspekten ausgehen: Was ist eine API? Welche Fähigkeiten hat die API?

    2.2. Definieren Sie den Synchronisierer Sync

    Sync erbt direkt AQS. Der Code lautet wie folgt:

    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;
          }
        }
      }
    }

    Der gesamte Code ist relativ klar:

    Die Beurteilung der Grenze, wie z Wenn der Eingabeparameter ungültig ist, wird dies nicht passieren, wenn die Sperre aufgehoben wird. Wir müssen solche Probleme beurteilen und die Strenge des Denkens widerspiegeln. Sie müssen die Form for spin + CAS verwenden, um sicherzustellen, dass beim gleichzeitigen Hinzufügen beim Sperren oder Freigeben der Sperre ein erneuter Versuch erfolgreich durchgeführt werden kann. Beim Schreiben für Spin müssen wir darauf achten, zum richtigen Zeitpunkt zurückzukehren und keine Endlosschleife zu verursachen. Schreiben Sie sie nicht selbst. Die CAS-Methode, die wir selbst schreiben, kann nicht garantiert werden.

    2.3. Ob der Link abgerufen werden kann, hängt davon ab, ob die Sperre abgerufen werden kann.

    Nachdem die Sperre definiert wurde, müssen wir die Sperre mit dem Abruf des MySQL-Links kombinieren ist hauptsächlich für zwei große Funktionen verantwortlich:

    Stellen Sie eine Verbindung mit MySQL über JDBC her.

    Kombiniert mit einer Sperre, um zu verhindern, dass die Gesamtzahl der MySQL-Links 10 überschreitet, wenn die Anfrage zu groß ist.

    Werfen wir zunächst einen Blick auf den MysqlConnection-Initialisierungscode:

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

    Wir können sehen, dass wir während der Initialisierung die maximale Anzahl von Links angeben und diesen Wert dann an die Sperre übergeben müssen, da die maximale Anzahl von Links beträgt der Status des ShareLock-Sperrwerts.

    Dann haben wir zur Vervollständigung von 1 eine private Methode geschrieben:

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

    Dann haben wir 2 implementiert, der Code lautet wie folgt:

    // 对外获取 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 释放失败。

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

    Was sind die Designstruktur und Details der Java-Rewrite-Sperre?

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

    Das obige ist der detaillierte Inhalt vonWas sind die Designstruktur und Details der Java-Rewrite-Sperre?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen