>  기사  >  Java  >  Java rewrite lock의 설계 구조와 세부 사항은 무엇입니까?

Java rewrite lock의 설계 구조와 세부 사항은 무엇입니까?

王林
王林앞으로
2023-04-18 17:22:031149검색

    소개

    어떤 면접관들은 학생들에게 자물쇠의 원리를 설명한 후 새 자물쇠를 다시 작성하라고 하고, 화이트보드에 전반적인 아이디어와 코드 논리를 적어달라고 요청하는 것을 좋아합니다. 개인적으로 두 부분에 중점을 두고 있다고 생각합니다.

    잠금 원리에 대한 이해가 어떻게 시작되었는지 살펴보세요. 소스 코드를 읽지 않았다면 온라인 기사를 읽어보거나 뒷면에 있는 인터뷰 질문을 통해서도 알 수 있습니다. 일반적인 원칙이지만 실제로 소스 코드를 본 적이 없거나 잠금 관련 프로젝트에 대한 경험이 없으면 그 자리에서 잠금 구현 코드를 작성하기가 어렵습니다.

    생성할 필요는 없습니다. Java 잠금의 기존 API를 모방하기 위해 다시 작성하면 됩니다.

    소스 코드를 읽어보셨다면 이 질문은 정말 간단합니다. 익숙한 자물쇠를 선택하여 따라하시면 ​​됩니다.

    1. 요구 사항

    일반적으로 잠금을 사용자 정의할 때 요구 사항에 따라 정의합니다. 공유 잠금의 경우 공유 리소스와 같은 다양한 시나리오를 생각할 수 있습니다. 데이터베이스 링크에 대한 공유 액세스와 같이 읽기 잠금을 공유할 수 있습니다. 예를 들어 소켓 서버의 링크 수를 공유할 수 있습니다. 잠금을 정의하기 위해 데이터베이스 링크에 대한 공유 액세스 시나리오를 선택합니다.

    2. 상세 설계

    우리의 데이터베이스가 10개의 연결만 지원할 수 있는 독립형 mysql이라고 가정합니다. 데이터베이스 링크를 생성할 때 가장 원시적인 JDBC 방법을 사용합니다. 인터페이스를 사용합니다. JDBC는 링크 생성 프로세스를 캡슐화합니다. 이 인터페이스의 이름은 Create Link Interface입니다.

    공유 액세스 데이터베이스 링크에 대한 전체 요구 사항은 다음과 같습니다. 결합된 모든 요청에 ​​대한 mysql 링크 수는 10(포함)을 초과할 수 없습니다. 10을 초과하면 오류가 직접 보고됩니다.

    이 맥락에서 우리는 다음 그림을 디자인했습니다.

    Java rewrite lock의 설계 구조와 세부 사항은 무엇입니까?

    이 디자인의 가장 중요한 부분은 잠금을 얻을 수 있는지 여부에 따라 mysql 링크를 얻을 수 있는지 여부를 결정한다는 것입니다. 그런 다음 링크를 얻을 수 있습니다. 그렇지 않으면 오류가 직접 보고됩니다.

    그럼 구현된 코드를 살펴보겠습니다.

    2.1. 잠금 정의

    먼저 정의에는 두 가지 요소가 필요합니다.

    잠금 정의: 외부에서 제공되는 동기화 잠금; 그리고 잠금 해제.

    공유 잠금의 코드 구현은 다음과 같습니다.

    // 共享不公平锁
    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 구현에는 잠금 및 해제를 위한 입력 매개변수가 없습니다. 이는 메서드에 1로 하드코딩되어 있으며, 이는 메서드가 실행될 때마다 잠금이 한 번만 잠기거나 해제될 수 있음을 의미합니다. 매개변수는 부울 값이고 true는 추가를 의미합니다. 잠금 또는 잠금 해제가 성공했음을 의미하고, false는 실패를 의미하며, 맨 아래 계층에서는 동기화 불공정 잠금을 사용합니다.

    위의 사고 방식에는 방법론이 있습니다. 즉, 문제에 대해 생각할 때 두 가지 측면에서 시작할 수 있습니다. API란 무엇입니까? API에는 어떤 기능이 있나요?

    2.2.동기화 장치 정의

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

    전체 코드에서 주의해야 할 점은 다음과 같습니다.

    경계에 대한 판단. 입력 매개변수가 불법인지, 잠금이 해제되면 발생하지 않을까요? 불법 상태 등의 경계 문제가 예상됩니다. 이러한 문제에 대한 판단을 내리고 사고의 엄격함을 반영해야 합니다.

    잠금을 해제하려면; , 동시 추가가 발생할 때 잠금을 잠그거나 해제할 때 성공적으로 재시도할 수 있도록 spin + CAS를 사용해야 합니다. Spin용으로 작성 시에는 무한 루프가 발생하지 않도록 주의가 필요하며, CAS 방식은 AQS에서 제공하는 것이므로 직접 작성하지 마십시오.

    2.3.링크를 얻을 수 있는지 여부는 잠금을 얻을 수 있는지 여부에 따라 결정됩니다

    잠금이 정의된 후에는 MysqlConnection이라는 Mysql 링크 도구 클래스를 작성했습니다. 주로 두 가지 큰 기능을 담당합니다:

    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 rewrite lock의 설계 구조와 세부 사항은 무엇입니까?

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

    위 내용은 Java rewrite lock의 설계 구조와 세부 사항은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제