我想知道解决这个问题的最有效方法是什么。
我有一个多线程数据库实现(例如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的构造函数完成之前不会有并发调用。 :)所以,所有并发都发生在构造函数之后。
答案 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.
}
}