使用嵌套锁同步

时间:2017-01-20 08:23:36

标签: java synchronization locking reentrantlock

有时,我发现在一些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;
}

这让我感到困惑,因为已经使用了第一个锁,为什么作者再次使用另一个锁?

3 个答案:

答案 0 :(得分:1)

第一个锁是“读”锁,第二个锁是某种“写”锁。因此,如果要在写入时避免锁定读取操作,这可能会很好,反之亦然。这可以改善性能。但是这种事情总是存在风险,并且可能会导致棘手的错误。如果您只能为读取和写入提供一个锁定,则可能更简单,更强大。

答案 1 :(得分:0)

根据名称,我建议外锁是一个readLock,内锁是一个WriteLock。两者都有不同的行为。由于可能存在尽可能多的readLock,但writeLock只能是与readLock和writeLocks兼容的单个锁。

所以在这个例子中他锁定阅读,所以没有他释放就不能写作(尽管在这种情况下可能有点矫枉过正)。通常会进行一些检查以确定是否应该执行写入(不是在这种情况下),之后需要执行writeLock来执行写入操作Threadsafe。

ReentrantLock是此类机制的默认实现,您可以使用它来观察/测试这些机制

答案 2 :(得分:0)

它增加了锁定粒度。

例如,假设您想要使用锁保护某些变量ab。你可以使用一个大锁:

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而另一个只读{/ 1}},则必须进行不必要的同步(例如,B将阻塞{{1}持有锁)。

使用两个锁,可以避免此问题:

getA()

如果您想同时保护setB()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(); } } } ,则需要同时保留两个锁。