2017-01-20 5 views
0

いつか、いくつかのJavaコードでは、ネストされたロックを使用して同期方法を実現しています。次のようなコードネストされたロックとの同期

// lock for appending state management 
final Lock appendLock = new ReentrantLock(); 
// global lock for array read and write management 
final ReadWriteLock arrayReadWritelock = new ReentrantReadWriteLock(); 
final Lock arrayReadLock = arrayReadWritelock.readLock(); 
final Lock arrayWriteLock = arrayReadWritelock.writeLock(); 

private MapEntry acquireNew(int length) throws IOException { 
    MapEntry mapEntry = null; 
    try { 
     arrayReadLock.lock(); //THE FIRST LOCK 
     IMappedPage toAppendIndexPage = null; 
     long toAppendIndexPageIndex = -1L; 
     long toAppendDataPageIndex = -1L; 
     long toAppendArrayIndex = -1L; 
     try { 
      appendLock.lock(); //THE SECOND LOCK 
      if (this.isFull()) { 
       throw new IOException("ring space of java long type used up, the end of the world!!!"); 
      } 
      if (this.headDataItemOffset + length > DATA_PAGE_SIZE) { 
       if (this.headDataPageIndex == Long.MAX_VALUE) { 
        this.headDataPageIndex = 0L; 
       } else { 
        this.headDataPageIndex++; 
       } 
       this.headDataItemOffset = 0; 
      } 
      toAppendDataPageIndex = this.headDataPageIndex; 
      .......... 
      .......... 
      mapEntry = new MapEntry(toAppendArrayIndex, length, toAppendIndexItemOffset, toAppendIndexPage, this.dataPageFactory); 
      mapEntry.MarkAllocated(); 
      this.totalEntryCount.incrementAndGet(); 
      this.totalSlotSize.addAndGet(length); 
      this.arrayHeadIndex.incrementAndGet(); 
      IMappedPage metaDataPage = this.metaPageFactory.acquirePage(META_DATA_PAGE_INDEX); 
      ByteBuffer metaDataBuf = metaDataPage.getLocal(0); 
      metaDataPage.setDirty(true); 
     } finally { 
      appendLock.unlock(); 
     } 
    } finally { 
     arrayReadLock.unlock(); 
    } 
    return mapEntry; 
} 

最初のロックが使用された理由は、著者がもう一度別のロックを使用するためです。

+0

@Kayaman arrayReadLockは最終ロックの配列です。arrayReadLock = arrayReadWritelock.readLock(); – lawrence

+0

さて、問題のコードを編集し続けると、何が起きているのかを実際には知りません。 – Kayaman

+0

@ Kayamanそれは私のせいで、質問を正確に記述していないのです。 – lawrence

答えて

1

最初のロックは「読み取り」ロックであり、2番目のロックはある種の「書き込み」ロックです。これは、書き込み時に読み取り操作をロックしないようにしたい場合には有効です(逆も同様です)。これにより、パフォーマンスが向上する可能性があります。しかし、この種のものでは常にリスクがあり、トリッキーなバグを引き起こす可能性があります。読み取りと書き込みの両方にロックが1つしかないのであれば、それはおそらくよりシンプルで強力です。

+0

もちろん、読み取り/書き込み用に別々のロックオブジェクトを使用する代わりに使用できる(またはすべき)ReadWriteLockがあります。 – Kayaman

+0

ありがとう@Kayaman @ developer33、私はコードを更新します。最後のロックappendLock =新しいReentrantLock(); //アレイの読み取りと書き込みの管理のためのグローバルロック final ReadWriteLock arrayReadWritelock = new ReentrantReadWriteLock(); – lawrence

+0

私は、最初のスレッドによってreadLockが取得された後、writeLockが取得される前に、seccondスレッドがwriteLockを要求しようとした場合に、デッドロックが発生するかどうかを調べるためにインターレスされます。 (ロックが公正なポリシーを使用しているシナリオで) – n247s

0

名前に基づいて、私は外側のロックがreadLockで、内部がWriteLockをロックすることをお勧めします。どちらも行動が異なります。可能な限り多くのreadLocksが存在する可能性がありますが、writeLockは読み取りロックと書き込みロックの両方に対応する単一のロックにしかなりません。

この例では、彼は読書のためにロックしているので、彼が解放されていないと書いてはいけません(この場合は少し過度かもしれませんが)。通常、書き込みを実行する必要があるかどうかを確認するためのチェックが行われます(この場合ではありません)。その後、WriteLockが書き込み動作Threadsafeを実行する必要があります。

ReentrantLockは、これらのメカニズムは、それは粒度をロック増加

0

あなたが/テストを観察するために使用することができた、そのようなメカニズムのデフォルトの実装です。

例えば、いくつかの変数abをロックで保護したいとします。あなたが一つの大きなロックを使用することができます。1つのスレッドが読み込む場合

public class BigLockDemo { 

    private final Lock bigLock = new ReentrantLock(); 
    private int a; 
    private int b; 

    public int getA() { 
     bigLock.lock(); 
     try { 
      return a; 
     } finally { 
      bigLock.unlock(); 
     } 
    } 

    public void setA(int a) { 
     bigLock.lock(); 
     try { 
      this.a = a; 
     } finally { 
      bigLock.unlock(); 
     } 
    } 

    public int getB() { 
     bigLock.lock(); 
     try { 
      return b; 
     } finally { 
      bigLock.unlock(); 
     } 
    } 

    public void setB(int b) { 
     bigLock.lock(); 
     try { 
      this.b = b; 
     } finally { 
      bigLock.unlock(); 
     } 
    } 

    public void setAB(int a, int b) { 
     bigLock.lock(); 
     try { 
      // nobody holding the lock may see simultaneously 
      // the new value of a and the old value of b 
      this.a = a; 
      this.b = b; 
     } finally { 
      bigLock.unlock(); 
     } 
    } 
} 

しかし、彼らは不必要に同期化する必要があり、/はaだけ書いて、他のは/だけBの読み書きを行う(setB()がロックを保持している間、たとえば、getA()はブロックされます) 。 2つのロックで

、あなたはこの問題を避けることができます:あなたはabの両方を保護したい場合には

public class ABLockDemo { 

    private final Lock aLock = new ReentrantLock(); 
    private final Lock bLock = new ReentrantLock(); 
    private int a; 
    private int b; 

    public int getA() { 
     aLock.lock(); 
     try { 
      return a; 
     } finally { 
      aLock.unlock(); 
     } 
    } 

    public void setA(int a) { 
     aLock.lock(); 
     try { 
      this.a = a; 
     } finally { 
      aLock.unlock(); 
     } 
    } 

    public int getB() { 
     bLock.lock(); 
     try { 
      return b; 
     } finally { 
      bLock.unlock(); 
     } 
    } 

    public void setB(int b) { 
     bLock.lock(); 
     try { 
      this.b = b; 
     } finally { 
      bLock.unlock(); 
     } 
    } 

    public void setAB(int a, int b) { 
     aLock.lock(); 
     bLock.lock(); 
     try { 
      // nobody holding the locks may see simultaneously 
      // the new value of a and the old value of b 
      this.a = a; 
      this.b = b; 
     } finally { 
      aLock.unlock(); 
      bLock.unlock(); 
     } 
    } 
} 

を、あなたは両方のロックを保持する必要があります。

+0

自分でテストできる完全なサンプルコードはありますか? – lawrence

関連する問題