I am dipping my feet in Futures. A Future can be created with a Runnable and with a Callable. Is there a way to decide how it was created?
For example, I have the following code:
Future<?> future = null;
Future<?> future2 = null;
ExecutorService service = null;
service = Executors.newSingleThreadExecutor();
future = service.submit(() -> {
for (int i = 0; i < 5; ++i) {
System.out.println("Printing record: " + i);
Thread.sleep(5);
}
return "Done";
});
future2 = service.submit(() -> System.out.println("Printing zoo inventory"));
System.out.println("================================================================");
System.out.println(future);
System.out.println(future.get().getClass());
System.out.println(future.get());
System.out.println("================================================================");
System.out.println(future2);
try {
System.out.println(future2.get().getClass());
System.out.println(future2.get());
} catch (ExecutionException e) {
System.out.println("Could not do a get");
}
System.out.println("================================================================");
This results in ending with:
================================================================
java.util.concurrent.FutureTask@5caf905d[Completed normally]
class java.lang.String
Done
================================================================
java.util.concurrent.FutureTask@1f32e575[Completed normally]
Exception in thread "main" java.lang.NullPointerException
at ZooInfo.main(ZooInfo.java:56)
I could solve this by using:
if (future2.get() == null) {
System.out.println("Made with a Runnable");
} else {
System.out.println(future2.get().getClass());
System.out.println(future2.get());
}
The problem with this is that when the Runnable still takes some time, I am waiting on the get for nothing. Is there a way to determine if a Future was created with a Runnable, or a Callable without resorting to using get()?
I don't believe that you really need to know whether the Future
was created from a Runnable
or a Callable
.
For one thing, there are more ways than that to create a Future
: for example, CompleteableFuture
is not created from either; and, more generally, since Future
is an interface, one can create instances however you like.
For another: the abstraction of Future
is that it is something that gives you a (possibly null) value when it completes, or throws an exception. That's all it is meant to do.
(Also, your current approach of checking for nullity of the return value doesn't work reliably because Callable.call()
is allowed to return null).
If you need it to do something else, you may want to revisit your design so you can simply treat it as it is intended.
But if you really do have a use case that does require you to know how it was created, you need to control the creation. Rather than letting callers submit code directly to the executor, wrap in a class like this:
class YourExecutor {
// Initialize in ctor.
private final ExecutorService executor;
FromRunnable submit(Runnable r) {
return new FromRunnable(executor.submit(r));
}
<T> FromCallable<T> submit(Callable<? extends T> c) {
return new FromCallable<>(executor.submit(c));
}
}
where FromRunnable
and FromCallable<T>
are classes implementing Future<Void>
and Future<T>
respectively, which delegate all of the methods to another instance of a compatible Future (passed as the constructor parameter).
You can then check the provenance using instanceof
; or by some other means, such as extending a common base case or interface which provides a method describing the provenance.
But, just to reiterate, a better approach is to design your code so it doesn't need to know.