Rumah  >  Artikel  >  Java  >  Apakah struktur reka bentuk dan butiran kunci tulis semula Java

Apakah struktur reka bentuk dan butiran kunci tulis semula Java

王林
王林ke hadapan
2023-04-18 17:22:031150semak imbas

    Pengenalan

    Sesetengah penemuduga suka meminta pelajar menulis semula kunci baharu selepas menerangkan prinsip kunci dan meminta mereka hadir di tempat kejadian . Tulis idea umum dan logik kod pada papan putih soalan temu bual ini secara peribadi saya fikir ia memfokuskan kepada dua bahagian:

    Periksa bagaimana pemahaman anda tentang prinsip kunci itu berlaku belum mentafsir kod sumber, anda hanya boleh membaca artikel dalam talian atau menghafal soalan temu duga, dan anda boleh memberitahu prinsip umum, tetapi sukar untuk anda menulis kod pelaksanaan kunci di tempat kejadian, melainkan anda benar-benar membaca kod sumber . Atau mempunyai pengalaman projek yang berkaitan dengan kunci;

    Kami tidak perlu mencipta, kami hanya perlu meniru dan menulis semula API sedia ada dalam kunci Java.

    Jika anda telah membaca kod sumber, soalan ini sangat mudah anda boleh memilih kunci yang anda biasa tiru.

    1. Keperluan

    Secara amnya, apabila menyesuaikan kunci, kami mentakrifkannya berdasarkan keperluan perkara. Senario, sebagai contoh, kunci baca pada sumber yang dikongsi boleh dikongsi, seperti akses dikongsi kepada pautan pangkalan data, contohnya, bilangan pautan pada pelayan Socket boleh dikongsi akses kepada pautan pangkalan data untuk menentukan Kunci.

    2. Reka bentuk terperinci

    Anggapkan (andaian berikut adalah semua andaian) bahawa pangkalan data kami adalah mysql yang berdiri sendiri, yang hanya boleh menanggung 10 sambungan apabila membuat pautan pangkalan data JDBC asal Dengan cara ini, kami menggunakan antara muka untuk merangkum proses mencipta pautan menggunakan JDBC Kami menamakan antara muka ini: buat antara muka pautan.

    Keperluan keseluruhan untuk pautan pangkalan data akses dikongsi adalah seperti berikut: bilangan pautan mysql untuk semua permintaan digabungkan tidak boleh melebihi 10 (termasuk apabila melebihi 10, ralat akan dilaporkan secara langsung).

    Dalam konteks ini, kami mereka bentuk gambar berikut:

    Apakah struktur reka bentuk dan butiran kunci tulis semula Java

    Bahagian paling kritikal dalam reka bentuk ini ialah sama ada kita boleh mendapatkan kunci melaluinya sama ada pautan mysql boleh diperoleh, jika kunci boleh diperoleh, maka pautan boleh diperoleh, jika tidak ralat akan dilaporkan terus.

    Kemudian mari kita lihat kod yang dilaksanakan:

    2.1 Tentukan kunci

    Mula-mula kita perlu menentukan kunci, yang memerlukan dua elemen:

    <.>Takrifan kunci: Penyegerakan penyegerak; kaedah mengunci dan membuka kunci yang disediakan oleh kunci kepada dunia luar.

    Pelaksanaan kod kunci kongsi adalah seperti berikut:

    // 共享不公平锁
    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);
      }
    }
    Seperti yang dapat dilihat daripada kod di atas, pelaksanaan mengunci dan melepaskan kunci bergantung pada pelaksanaan asas penyegerak Segerakkan.

    Satu-satunya perkara yang perlu diberi perhatian ialah kunci perlu menentukan spesifikasi API, terutamanya dalam dua aspek:

    Apa yang diperlukan oleh API ialah parameter yang anda perlu berikan kepada saya apabila kunci dimulakan Apabila memulakan ShareLock, anda perlu lulus bilangan maksimum kunci boleh kongsi

    perlu menentukan keupayaannya sendiri, iaitu, mentakrifkan parameter input dan parameter output bagi setiap kaedah. Dalam pelaksanaan ShareLock, tiada parameter input untuk mengunci dan melepaskan kunci Ia dikodkan dengan keras 1 dalam kaedah, yang bermaksud bahawa setiap kali kaedah itu dilaksanakan, kunci hanya boleh dikunci sekali atau dilepaskan parameter output ialah nilai Boolean, dan benar bermakna menambah Kunci atau pelepasan kunci berjaya, palsu menunjukkan kegagalan dan lapisan bawah menggunakan kunci tidak adil Segerak.

    Cara berfikir di atas mempunyai metodologi, iaitu apabila kita memikirkan sesuatu masalah, kita boleh bermula dari dua aspek: Apakah itu API? Apakah keupayaan yang ada pada API?

    2.2. Tentukan Penyegerakan penyegerak

    Penyegerakan terus mewarisi AQS, kodnya adalah seperti berikut:

    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;
          }
        }
      }
    }
    Kod keseluruhannya agak jelas, apa yang perlu kita bayar perhatian ialah:

    Penghakiman sempadan, seperti sama ada parameter input adalah menyalahi undang-undang, sama ada akan dijangkakan keadaan menyalahi undang-undang semasa melepaskan kunci, dan isu sempadan lain Kita perlu menilai isu sedemikian untuk mencerminkan ketegasan berfikir;

    Untuk mengunci dan melepaskan kunci, anda perlu menggunakan bentuk untuk putaran + CAS untuk memastikan anda boleh mencuba semula dengan jayanya apabila mengunci atau melepaskan kunci secara serentak. Apabila menulis untuk putaran, kita perlu memberi perhatian untuk kembali pada masa yang sesuai dan tidak menyebabkan gelung tak terhingga Kaedah CAS telah disediakan oleh AQS. Kaedah CAS yang kita tulis sendiri tidak dapat menjamin atomicity.

    2.3 Sama ada pautan boleh diperolehi ditentukan oleh sama ada kunci boleh diperolehi

    Selepas kunci ditakrifkan, kita perlu menggabungkan kunci dengan mendapatkan pautan Mysql Kelas alat pautan Mysql , dipanggil MysqlConnection, yang bertanggungjawab terutamanya untuk dua fungsi utama:

    Mewujudkan pautan dengan Mysql melalui JDBC

    Digabungkan dengan kunci untuk mengelakkan jumlah pautan Mysql daripada melebihi; 10 apabila permintaan terlalu besar.

    Pertama, mari kita lihat kod permulaan MysqlConnection:

    public class MysqlConnection {
      private final ShareLock lock;
      // maxConnectionSize 表示最大链接数
      public MysqlConnection(int maxConnectionSize) {
        lock = new ShareLock(maxConnectionSize);
      }
    }
    Kita dapat melihat bahawa semasa permulaan, kita perlu menentukan bilangan maksimum pautan, dan kemudian hantar nilai ini kepada kunci, Kerana bilangan maksimum pautan ialah nilai keadaan kunci ShareLock.

    Kemudian untuk melengkapkan 1, kami menulis kaedah peribadi:

    // 得到一个 mysql 链接,底层实现省略
    private Connection getConnection(){}
    Kemudian kami melaksanakan 2, kodnya adalah seperti berikut:

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

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

    Apakah struktur reka bentuk dan butiran kunci tulis semula Java

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

    Atas ialah kandungan terperinci Apakah struktur reka bentuk dan butiran kunci tulis semula Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan:
    Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam