Search code examples
javafuturetask

Java FutureTask - Multithreaded call to get()


I have the following two methods in a 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; 
}

I wanted to do the following:

1) Do a cache eviction using periodEviction() every one hour or so.

2) Otherwise, use the cached value when db loading is done.

I believe I have misunderstood Java future as I couldn't answer the question, "What happens when Thread A,B,and C all are calling loadMyItems() at the same time?"

So does this mean without something like an executor, this implementation is still not thread safe?


Solution

  • An even simpler approach is to not cache the object at all but just retain the 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();
    }
    

    With the caveat that this will trigger the database query every eviction period rather than on demand.