有效地等待资源

时间:2014-08-01 14:46:10

标签: java multithreading asynchronous

我想知道解决这个问题的最有效方法是什么。

我有一个多线程数据库实现(例如LevelDB),我希望它能够处理同步,因为它可以做得更好。但是,我想异步初始化数据库,而不阻塞任何线程,除非它们在打开之前以某种方式想要使用数据库。

有些事情:

public class Storage {
  Database db;

  public Storage() {
    open();
  }

  private void open() {
    new Thread(new Runnable() {
      public void run() {
        // attempt to open db here, i.e. change the value of Storage.db from null
        // into Object
      }
    }).run();
  }

  public void accessMethod() {
    // this method should only use a non-null Storage.db value, it should block
    // until the thread above does not set the value of db to be an Object
  }

  public void nonAccessMethod() {
    // this method is not concerned with the value inside Storage.db and should not 
    // block while the thread above is running
    // example: memory cached operations on the db which will be executed after
    // the thread above finishes and "unlocks" Storage.db
  }
}

我提出了这个解决方案,但效率不高:

public class Storage {
  ReentrantLock lock;

  Database db;

  public Storage() {
    lock = new ReentrantLock();

    open();
  }

  private void open() {
    lock.lock(); // to be released in thread below

    new Thread(new Runnable() {
      public void run() {
        // heavy work here while populating Storage.db

        lock.unlock();
      }
    }).run();
  }

  // returns true if the database is not yet open and that we need to release
  // the lock once our code segment completes
  private boolean blockIfNotOpen() {
    if (lock.tryLock()) {
      lock.unlock();       // <<   this code segment sucks

      return false;
    } else {
      lock.lock();

      return true;
    }
  }

  public void accessMethod() {
    boolean wasNotOpen = blockIfNotOpen();

    // "blocking" code here

    if (wasNotOpen) {
      lock.unlock();
    }
  }

  public void nonAccessMethod() {
    // not concerned with Storage.db and therefore not trying to lock
  }
}

我不喜欢这个解决方案,因为在填充Storage.db之后,它仍然会在存储实现中同步对数据库的访问,而实际上DB内部有一个可以更好地处理并发的底层系统(示例:DB公开工作线程等。)。

在Storage对象上进行同步不是一个解决方案,因为它会一直很好地同步,而不仅仅是在Storage.db为空时。

注意:如果您担心锁定,我保证在Storage的构造函数完成之前不会有并发调用。 :)所以,所有并发都发生在构造函数之后。

1 个答案:

答案 0 :(得分:2)

我认为解决方案是在初始化db之后在构造函数ReadWriteLock中使用writeLock().lock() - writeLock().unlock()并且从db使用者使用readLock()

另一种选择,使用Future:

public class Storage {
    private final Future<Database> dbFuture = Executors.newSingleThreadExecutor().submit(
        new Callable<Database>()
        {
            public Database call()
            {
               return new Database(...);//Long running DB initialisation
            }
        }
    );

    public void accessMethod()
    {
       Database db = dbFuture.get();// will wait while the call() is not completed yet.
    }
}