3
\$\begingroup\$

I did not like that get synchronizes every time I call it - because I may be calling it more than once. So I wrote the wrapper for the future interface which keeps not synchronized local variable for completion state and caches the result. I am very sure it is thread safe. Please have a look:

package i;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class FutureTaskWrapper<V> implements RunnableFuture<V> {

    private FutureTask<V> localFuture;
    private V result;
    //this one is not synchronized on purpose. It does not have to be.
    //It will either use wrapped class' done mechanism or eventually will     update and it will use cached result.
    private boolean done = false;


    public FutureTaskWrapper(FutureTask<V> instance) {
        localFuture = instance;
    }

    @Override
    public void run() {
        localFuture.run();
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return localFuture.cancel(mayInterruptIfRunning);
    }

    @Override
    public boolean isCancelled() {
        return localFuture.isCancelled();
    }

    @Override
    public boolean isDone() {
        if (done) {
            return true;
        } else {
            return localFuture.isDone();
        }
    }

    @Override
    public V get() throws InterruptedException, ExecutionException {
        if (done) {
            System.out.println("using cached version.");
            return result;
        } else {
            System.out.println("using version version from future.");
            result = localFuture.get();
            done = true;
            return result;
        }
    }

    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        if (done) {
            System.out.println("using cached version.");
            return result;
        } else {
            System.out.println("using version version from future.");
            result = localFuture.get(timeout, unit);
            //could clean up localFuture here.
            done = true;
            return result;
        }
    }

    //main method for testing.
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        FutureTask<String> future = new FutureTask<>(() -> {
            Thread.sleep(1000L);
            return "future";
        });
        FutureTaskWrapper<String> wrapper = new FutureTaskWrapper(future);
        executor.submit(future);
        wrapper.get();
        wrapper.get();
        wrapper.get();
        wrapper.get();

        executor.shutdownNow();
    }
}
\$\endgroup\$
2
\$\begingroup\$

I am very sure it is thread safe.

Actually, it's not because the done is not in happens-before relations with the result.

But you can reach the desired behavior without extending anything: just check isDone() and call get() only if it was true.

\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer! I have made another version of the same. please have a look codereview.stackexchange.com/questions/205610/… the main difference is that variable called done just result. Plus documented better. Thanks! \$\endgroup\$ – Audrius Oct 15 '18 at 15:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.