我想知道解决这个问题最有效的方法是什么。
我有一个多线程数据库实现(例如 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 为 null 时同步。
注意:如果您担心锁,我保证在 Storage 的构造函数完成之前不会有并发调用。 :) 因此,所有并发都发生在构造函数之后。
最佳答案
我认为解决方案是在构造函数中使用 ReadWriteLock
- writeLock().lock()
,writeLock().unlock()
在数据库初始化并从数据库使用者使用 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.
}
}
关于java - 高效等待资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25082815/