我有这样的东西:
private Striped<ReadWriteLock> stripes = Striped.lazyWeakReadWriteLock(STRIPES_AMOUNT);
private final Cache<Long, BlockingDeque<Peer>> peers = CacheBuilder.newBuilder()
.expireAfterWrite(PEER_ACCESS_TIMEOUT_MIN, TimeUnit.MINUTES)
.build();
每次我对缓存进行操作时,我都会在stripes
的帮助下锁定它。
示例#1 [写入操作]
public void removePeers(long sessionId) {
Lock lock = stripes.get(sessionId).writeLock();
lock.lock();
try {
peers.invalidate(sessionId);
} finally {
lock.unlock();
}
}
示例#2 [读取操作]
public BlockingDeque<Peer> getPeers(long sessionId) {
Lock lock = stripes.get(sessionId).readLock();
lock.lock();
try {
return peers.getIfPresent(sessionId);
} finally {
lock.unlock();
}
}
示例#3 [写入操作]
public boolean addPeer(Peer peer) {
long key = peer.getSessionId();
Lock lock = stripes.get(key).writeLock();
lock.lock();
try {
BlockingDeque<Peer> userPeers = peers.getIfPresent(key);
if (userPeers == null) {
userPeers = new LinkedBlockingDeque<Peer>();
peers.put(key, userPeers);
}
return userPeers.offer(peer);
} finally {
lock.unlock();
}
}
问:锁定以下方法的最有效方法是什么?
/**
* I should get the whole peers in cache
*/
public BlockingDeque<Peer> getAllPeers() {
BlockingDeque<Peer> result = new LinkedBlockingDeque<Peer>();
for (BlockingDeque<Peer> deque : peers.asMap().values()) {
result.addAll(deque);
}
return result;
}
最有效的方法是完全不锁定:)
请参阅我在代码审查中更新的答案:您不需要ReadWriteLock
,因此您无需锁定读取。
当您读取(例如在线程 1 中)时,您将获得当时缓存中内容的快照。如果您有并发修改(在线程 2 中),即使您使用锁,缓存的内容也可能在线程 1 完成其计算之前发生了变化,并且锁不会为您购买任何东西:
Thread 1 Thread 2
| |
getAllPeers |
| addPeer
Do something with |
the Peers but not |
the added one |
| |
因此,您对getAllPeers()
的实现很好。
作为旁注,如果您需要将所有条纹锁定在一个 Striped<Lock>
中,您可以使用 getAt()
来完成,但如果其中一个lock()
调用抛出未经检查的异常(这是允许的),这种天真的方式可能会给您带来麻烦:
for (int i = 0, size = stripes.size(); i++; i < size) {
stripes.getAt(i).lock();
}
try {
// Do something
} finally {
for (int i = 0, size = stripes.size(); i++; i < size) {
stripes.getAt(i).unlock();
}
}
另一种方法是递归执行此操作,但它会增加堆栈的长度条带数,因此如果您有大量条带,则可以获得StackOverflowException
:
public void doSomethingWithLocks() {
doSomethingWithLock(0);
}
private void doSomethingWithLock(int stripe) {
if (stripe < stripes.size()) {
Lock lock = stripes.getAt(stripe);
lock.lock();
try {
doSomethingWithLock(stripe + 1);
} finally {
lock.unlock();
}
} else {
doSomething();
}
}
private void doSomething() {
// Do something
}