Java FutureTask-对get()的多线程调用

时间:2018-06-28 08:55:10

标签: java futuretask

我在课堂上有以下两种方法:

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的未来有误解,因为我无法回答以下问题:“当线程A,B和C都同时调用loadMyItems()时会发生什么?”

那么这是否意味着如果没有像执行程序之类的东西,该实现仍然不是线程安全的?

2 个答案:

答案 0 :(得分:1)

一种超级简单的方法是将loadMyItems声明为synchronized。但是,如果类具有访问myDef的其他方法,则也必须声明那些synchronized。有时,这会导致非常粗粒度的锁定并降低性能。

如果您要寻找最干净/最快的代码,而不是将periodEviction声明为synchronized,而是将myDef声明为AtomicReference

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会被窃听)在中间)。

答案 1 :(得分:1)

一种更简单的方法是根本不缓存对象,而只保留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();
}

需要警告的是,这将在每个驱逐期间而不是按需触发数据库查询。