Java FutureTask - 对 get() 的多线程调用
Java FutureTask - Multithreaded call to get()
我在一个class中有以下两种方法:
private MyDef myDef;
private FutureTask<MyDef> defFutureTask;
public synchronized void periodEviction() {
myDef = null;
}
public MyDef loadMyItems() {
// if it's not ready use a future - it will block until the results are ready
if (this.myDef == null) { // this will still not be thread safe
Callable<MyDef> callableDef = ()->{ return this.loadFromDatabase(); };
FutureTask<MyDef> defTask = new FutureTask<>(callableDef);
this.defFutureTask = defTask;
defFutureTask.run();
}
try {
// wait until's it's ready
this.myDef = this.qDefFuture.get();
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return this.myDef;
}
我想执行以下操作:
1) 使用 periodEviction()
每隔一小时左右进行一次缓存逐出。
2) 否则,在数据库加载完成时使用缓存值。
我相信我误解了 Java 未来,因为我无法回答这个问题,"What happens when Thread A,B,and C all are calling loadMyItems()
at the same time?"
那么这是否意味着没有像执行程序这样的东西,这个实现仍然不是线程安全的?
一个超级简单的方法是将 loadMyItems
声明为 synchronized
。但是如果 class 有其他访问 myDef
的方法,您也必须声明那些 synchronized
。有时这会导致非常粗粒度的锁定和较慢的性能。
如果您要查找 cleanest/fastest 代码,请将 myDef
声明为 AtomicReference
,而不是将 periodEviction
声明为 synchronized
:
private final AtomicReference<MyDef> myDef = new AtomicReference<>();
那么periodEviction
的正文是:
synchronized (myDef) {
myDef.set(null);
}
而 loadMyItems
的正文是:
synchronized (myDef) {
if (myDef.get() == null) {
// perform initialization steps, ending with:
myDef.set(this.qDefFuture.get());
}
return myDef.get();
}
如果许多线程同时调用 loadMyItems
,myDef
只会被初始化一次,并且它们都将返回相同的对象(除非以某种方式调用 periodEviction
偷偷夹在中间)。
一个更简单的方法是根本不缓存对象而只保留 Future
.
private CompletableFuture<MyDef> defFuture;
public synchronized void periodEviction() {
// evict by triggering the request anew
defFuture = CompletableFuture.supplyAsync(this::loadFromDatabase);
}
public synchronized Optional<MyDef> loadMyItems() {
try {
return Optional.of(this.defFuture.get());
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return Optional.empty();
}
需要注意的是,这将在每个驱逐期而不是按需触发数据库查询。
我在一个class中有以下两种方法:
private MyDef myDef;
private FutureTask<MyDef> defFutureTask;
public synchronized void periodEviction() {
myDef = null;
}
public MyDef loadMyItems() {
// if it's not ready use a future - it will block until the results are ready
if (this.myDef == null) { // this will still not be thread safe
Callable<MyDef> callableDef = ()->{ return this.loadFromDatabase(); };
FutureTask<MyDef> defTask = new FutureTask<>(callableDef);
this.defFutureTask = defTask;
defFutureTask.run();
}
try {
// wait until's it's ready
this.myDef = this.qDefFuture.get();
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return this.myDef;
}
我想执行以下操作:
1) 使用 periodEviction()
每隔一小时左右进行一次缓存逐出。
2) 否则,在数据库加载完成时使用缓存值。
我相信我误解了 Java 未来,因为我无法回答这个问题,"What happens when Thread A,B,and C all are calling loadMyItems()
at the same time?"
那么这是否意味着没有像执行程序这样的东西,这个实现仍然不是线程安全的?
一个超级简单的方法是将 loadMyItems
声明为 synchronized
。但是如果 class 有其他访问 myDef
的方法,您也必须声明那些 synchronized
。有时这会导致非常粗粒度的锁定和较慢的性能。
如果您要查找 cleanest/fastest 代码,请将 myDef
声明为 AtomicReference
,而不是将 periodEviction
声明为 synchronized
:
private final AtomicReference<MyDef> myDef = new AtomicReference<>();
那么periodEviction
的正文是:
synchronized (myDef) {
myDef.set(null);
}
而 loadMyItems
的正文是:
synchronized (myDef) {
if (myDef.get() == null) {
// perform initialization steps, ending with:
myDef.set(this.qDefFuture.get());
}
return myDef.get();
}
如果许多线程同时调用 loadMyItems
,myDef
只会被初始化一次,并且它们都将返回相同的对象(除非以某种方式调用 periodEviction
偷偷夹在中间)。
一个更简单的方法是根本不缓存对象而只保留 Future
.
private CompletableFuture<MyDef> defFuture;
public synchronized void periodEviction() {
// evict by triggering the request anew
defFuture = CompletableFuture.supplyAsync(this::loadFromDatabase);
}
public synchronized Optional<MyDef> loadMyItems() {
try {
return Optional.of(this.defFuture.get());
} catch(InterruptedException e) {
log.error(this.getClass(), "Interrupted whilst getting future..");
} catch(ExecutionException e) {
log.error(this.getClass(), "Error when executing callable future");
}
return Optional.empty();
}
需要注意的是,这将在每个驱逐期而不是按需触发数据库查询。