Thursday, November 7, 2013

Synchronous Executor Service

Recently I found myself in a situation where I wanted my code to be asynchronous, but only in certain situations. The solution I came up with is a synchronous Executor

public class SynchronousExecutor implements ExecutorService {

 @Override
 public void execute(Runnable command) {
  throw new UnsupportedOperationException();
 }

 @Override
 public void shutdown() {
  throw new UnsupportedOperationException();
 }

 @Override
 public List<Runnable> shutdownNow() {
  throw new UnsupportedOperationException();
 }

 @Override
 public boolean isShutdown() {
  throw new UnsupportedOperationException();
 }

 @Override
 public boolean isTerminated() {
  throw new UnsupportedOperationException();
 }

 @Override
 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
  throw new UnsupportedOperationException();
 }

 @Override
 public <T> Future<T> submit(Callable<T> task) {
  T t = null;
  Throwable exception = null;
  
  try {
   t = task.call();
  } catch (Throwable e) {
   exception = e;
  }

  return new SynchronousFuture<T>(t, exception);
 }

 @Override
 public <T> Future<T> submit(Runnable task, T result) {
  throw new UnsupportedOperationException();
 }

 @Override
 public Future<?> submit(Runnable task) {
  throw new UnsupportedOperationException();
 }

 @Override
 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
  throw new UnsupportedOperationException();
 }

 @Override
 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
  throw new UnsupportedOperationException();
 }

 @Override
 public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
  // TODO Auto-generated method stub
  throw new UnsupportedOperationException();
 }

 @Override
 public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException,
   ExecutionException, TimeoutException {
  throw new UnsupportedOperationException();
 }
}

This implements ExecutorService but I've changed the behavior of submit, by executing the Callable, synchronously. The submit method returns a SynchronousFuture, which contains the return value of the Callable, or a Throwable if the call failed. I return my SynchronousExecutor in getDefaultExecutorService, however In situations where I want asynchronous execution, I override this method and return a standard ExecutorService.

 protected ExecutorService getDefaultExecutorService() { 
  return new SynchronousExecutor();
 }

The SynchronousExecutor is entirely stateless so we really could return the same instance every time instead of creating new objects. SynchronousFuture is pretty basic; many of the Future methods do not apply for synchronous execution, so I indicate that by throwing UnsupportedOperationException.

public class SynchronousFuture<t> implements Future<t> {

 private T t;
 private Throwable e;
 
 public SynchronousFuture(T t, Throwable e) {
  this.t = t;
  this.e = e;
 }
 
 @Override
 public boolean cancel(boolean mayInterruptIfRunning) {
  throw new UnsupportedOperationException();
 }

 @Override
 public boolean isCancelled() {
  return false;
 }

 @Override
 public boolean isDone() {
  return true;
 }

 @Override
 public T get() throws InterruptedException, ExecutionException {
  if (e != null) {
   throw new ExecutionException(e);
  }
  
  return t;
 }

 @Override
 public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
  throw new UnsupportedOperationException();
 }
}

Now we can submit Callables, just as you would to a typical ExecutorService, but it is executed synchronously. The call to the get method of SynchronousFuture never blocks since it's already complete.

No comments:

Post a Comment