使用ReentrantReadWriteLock和布尔标志

时间:2010-12-07 18:29:16

标签: java caching concurrency reentrantreadwritelock

我有一个缓存,可以预先加载大量数据(通过后台线程),并且在完全之前无法使用(它也会经常重新加载并在该加载期间无法使用)。我希望使用它的类在访问之前检查标志isLoaded()。为了简单起见,我使用ReentrantReadWriteLock(我在代码中省略了这一点)进行访问控制,如下所示:

public class Cache {

   private volatile boolean loaded = false; //starts false

   private static String[] cache;

   private static Lock readLock;
   private static Lock writeLock;

   public Object get(Object key) {
       if (!readLock.tryLock()) throw IllegalStateException(...);
       try {
           ... do some work
       } finally {
           readLock.unlock();
       }
   }

   // called by background thread
   private void loadFull() {
      loaded = false;
      writeLock.lock()
      try {
          cache = new String[];
          ... fill cache
      } finally {
          writeLock.unlock();
          loaded = true;
      }
   }
....
}  

现在在我的另一个课程中,我有一个这样的块:

if (cache.isLoaded()) {
    try {
      Object x = cache.get(y);
    } catch (IllegalStateException iex) {
      // goto database for object
    }
} else {
    // goto database for object
}

我真的需要try/catch吗?是否有可能将该标志设置为false并且readLock try()将失败?我是否应该打扰标志和jut捕获异常(因为我基本上做了相同的代码,如果抛出异常,就好像标志是假的)。我只是觉得我做了一些稍微错误的事情,但我不能指责它。感谢。

3 个答案:

答案 0 :(得分:2)

  

我真的需要try / catch吗?是吗   国旗将永远是可能的   设置为false并且readLock尝试()   会失败吗?

是的,你需要它。在调用时间cache.isLoaded()cache.get()之间,作者可以进入并获得写锁定 - 在这种情况下,cache.isLoaded()将返回true,但是cache.get()将抛出异常。

  

我是否应该打扰国旗和   jut捕获异常(因为我   如果是,基本上做相同的代码   抛出异常就像标志一样   假)。

从您显示的代码中,仅在get无法获取读锁定的情况下抛出异常。只有当时有并发写入器时,才会获取读锁定失败。在这种情况下,isLoaded也会返回false。所以仅仅依靠异常就足够了。另外,请考虑创建专门的CacheStaleException

答案 1 :(得分:1)

如果某个其他线程已经获得该锁,则tryLock将失败。这通常意味着如果客户端由于高争用而无法获取锁(多个客户端访问相同的缓存),则会抛出异常。您是否在客户层中实施了处理此类情况的后备策略?

另外,为什么static会锁定?我认为即使您的缓存通常在应用程序中用作单例,也不需要通过使Locks静态来限制其可用性。

答案 2 :(得分:0)

不,但说实话,你的范例令人困惑。据推测,去实际的数据库是昂贵的,这是缓存的目的。在重新加载缓存的情况下,等到它是不是更好?

假设你确实想要进入数据库,如果没有立即可用的读锁定,我会这样做:

   public Object get(Object key) {
       Object returnValue;
       if (readLock.tryLock()) {
           try {
               ... do some work
               returnValue = ...
           } finally {
               readLock.unlock();
           }
       } else {
           //go to database
           returnValue = ...
       }
       return returnValue;
   }