锁定缓存键

时间:2010-09-21 02:10:13

标签: caching locking key

我已经阅读了几个与此类似的问题,但没有一个答案提供了如何在保持锁完整性的同时清理内存的想法。我估计在给定时间内键值对的数量为数万,但数据结构生命周期内键值对的数量实际上是无限的(实际上它可能不会更多)超过十亿,但我编码到最坏的情况)。

我有一个界面:

public interface KeyLock<K extends Comparable<? super K>> {

  public void lock(K key);

  public void unock(K key);

}

使用默认实现:

public class DefaultKeyLock<K extends Comparable<? super K>> implements KeyLock<K> {

  private final ConcurrentMap<K, Mutex> lockMap;

  public DefaultKeyLock() {
    lockMap = new ConcurrentSkipListMap<K, Mutex>();
  }

  @Override
  public void lock(K key) {
    Mutex mutex = new Mutex();
    Mutex existingMutex = lockMap.putIfAbsent(key, mutex);
    if (existingMutex != null) {
      mutex = existingMutex;
    }
    mutex.lock();
  }

  @Override
  public void unock(K key) {
    Mutex mutex = lockMap.get(key);
    mutex.unlock();
  }

}

这很好用,但地图永远不会被清理干净。到目前为止,干净实施的目的是:

public class CleanKeyLock<K extends Comparable<? super K>> implements KeyLock<K> {

  private final ConcurrentMap<K, LockWrapper> lockMap;

  public CleanKeyLock() {
    lockMap = new ConcurrentSkipListMap<K, LockWrapper>();
  }

  @Override
  public void lock(K key) {
    LockWrapper wrapper = new LockWrapper(key);
    wrapper.addReference();
    LockWrapper existingWrapper = lockMap.putIfAbsent(key, wrapper);
    if (existingWrapper != null) {
      wrapper = existingWrapper;
      wrapper.addReference();
    }

    wrapper.addReference();
    wrapper.lock();
  }

  @Override
  public void unock(K key) {
    LockWrapper wrapper = lockMap.get(key);
    if (wrapper != null) {
      wrapper.unlock();
      wrapper.removeReference();
    }
  }

  private class LockWrapper {

    private final K key;

    private final ReentrantLock lock;

    private int referenceCount;

    public LockWrapper(K key) {
      this.key = key;
      lock = new ReentrantLock();
      referenceCount = 0;
    }

    public synchronized void addReference() {
      lockMap.put(key, this);
      referenceCount++;
    }

    public synchronized void removeReference() {
      referenceCount--;
      if (referenceCount == 0) {
        lockMap.remove(key);
      }
    }

    public void lock() {
      lock.lock();
    }

    public void unlock() {
      lock.unlock();
    }
  }

}

这适用于访问单个密钥锁的两个线程,但是一旦引入第三个线程,就不再保证锁完整性。有什么想法吗?

2 个答案:

答案 0 :(得分:0)

我不认为这适用于两个线程。考虑一下:

  • (线程A)调用lock(x),现在持有lock x
  • 线程切换
  • (线程B)调用lock(x),putIfAbsent()返回x的当前包装器
  • 线程切换
  • (线程A)调用unlock(x),包装器引用计数达到0并从地图中删除
  • (线程A)调用lock(x),putIfAbsent()为x插入一个新的包装器
  • (线程A)锁定新包装器
  • 线程切换
  • (线程B)锁定旧包装器

怎么样:

  • LockWrapper以引用计数1
  • 开头
  • 如果引用计数为0
  • ,则addReference()返回false
  • 在lock()中,如果existingWrapper!= null,我们在其上调用addReference()。如果返回false,则它已从地图中删除,因此我们循环返回并再次尝试putIfAbsent()

答案 1 :(得分:0)

我会默认使用固定数组作为条带锁,因为您可以将其调整为您期望的并发级别。虽然可能存在哈希冲突,但好的吊具会解决这个问题。如果锁用于短临界区,那么您可能会在ConcurrentHashMap中创建争用优化的争用。

欢迎您调整我的实现,但我只是为了好玩而实现了动态版本。它在实践中似乎没有用,因此只有固定用于生产。您可以使用ConcurrentHashMap中的hash()函数来提供良好的传播。

ReentrantStripedLock in, http://code.google.com/p/concurrentlinkedhashmap/wiki/IndexableCache