有时,我发现在一些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 :(得分:1)
第一个锁是“读”锁,第二个锁是某种“写”锁。因此,如果要在写入时避免锁定读取操作,这可能会很好,反之亦然。这可以改善性能。但是这种事情总是存在风险,并且可能会导致棘手的错误。如果您只能为读取和写入提供一个锁定,则可能更简单,更强大。
答案 1 :(得分:0)
根据名称,我建议外锁是一个readLock,内锁是一个WriteLock。两者都有不同的行为。由于可能存在尽可能多的readLock,但writeLock只能是与readLock和writeLocks兼容的单个锁。
所以在这个例子中他锁定阅读,所以没有他释放就不能写作(尽管在这种情况下可能有点矫枉过正)。通常会进行一些检查以确定是否应该执行写入(不是在这种情况下),之后需要执行writeLock来执行写入操作Threadsafe。
ReentrantLock是此类机制的默认实现,您可以使用它来观察/测试这些机制
答案 2 :(得分:0)
它增加了锁定粒度。
例如,假设您想要使用锁保护某些变量a
和b
。你可以使用一个大锁:
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();
}
}
}
,则需要同时保留两个锁。