These probably don't qualify as canonical use cases, but they are applications of executors that I have come across in real life.

Converting a blocking request into non-blocking request

I have a service interface with a method that blocks, possibly for a long time, for example, the client side of an HTTP-based service that blocks while waiting for a response.

    public interface Request ...
    public interface Response ...

    interface BlockingService {
        Response serve(Request req) throws ServiceException;
    }

I want an adapter that converts this interface into one that returns a Future<Response> without blocking.

    interface NonBlockingService {
        Future<Response> serve(Request req);
    }

Here's how I might do it under the currently checked in proposal.

    import java.util.concurrent.Callable;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;

    class NonBlockingServiceAdapter implements NonBlockingService {

        public NonBlockingServiceAdapter(BlockingService svc) {
            this(svc, null);
        }

        public NonBlockingServiceAdapter(BlockingService svc, Executor executor) {
            this.blockingService = svc;
            if (executor == null) {
                this.executor = Executors.newFixedThreadPool(3);
            }
            else {
                this.executor = executor;
            }
        }

        public Future<Response> serve(final Request req) {
            Callable<Response> task = new Callable<Response>() {
                public Response call() {
                    return blockingService.serve(req);
                }
            };
            return Executors.execute(executor, task);
        }

        private final BlockingService blockingService;
        private final Executor executor;
    }

If I change my mind about using a fixed size thread pool as the default and want to use, say, a caching thread pool, I don't have to change the imports and I don't have to learn any new types; I just consult the Executors javadocs and change one line to something like:

    this.executor = Executors.newCachedThreadPool();

In this case it is irrelevant that the factory method returns a more specific type.

If there were no Executor factory methods or if those factory methods were part of AbstractExecutor/ThreadExecutor, I'd have to learn about and import a type just so I could get access to its factory methods or public constructors.

If I have more complicated requirements on the internal executor, I can pass in an instance that has been configured in advance with Callbacks/Hooks/Intercepts, possibly an extension of an existing class or even my own custom implementation of Executor.

If a later redesign requires lifecycle management for the internal executor, under the current proposal I would change its type to ExecutorService and provide methods on NonBlockingServiceAdapter that ultimately call into the ExecutorService instance. The executor and the executor service are the same object under the current proposal, but this is not a requirement for this example.

Workaround Borland C++ DLL threading limitation

An annoying design flaw in one version of the Borland C++ (for Windows) runtime libraries causes a severe crash.if a C++ exception is thrown in DLL code within a thread other than the one in which the DLL was loaded. For JNI users, there are several ways around this problem, including changing the compiler and avoiding the use of C++ exceptions. However, if none of these workarounds are available, the only solution is to make sure that alls calls to native methods implemented by such a DLL are made in the same thread as the one that loaded the DLL.

If you run into this situation repeatedly, as I did, you might find yourself wanting an adapter class to do the heavy lifting for you using a dynamic proxy, as follows: For a class NativeImpl with native methods implemented by a Borland C++ DLL, define an interface NativeInterface consisting of all of the native methods of NativeImpl, and make NativeImpl extend NativeInterface. The adapter uses Proxy.newProxyInstance to create a new instance of this interface by implementing the invoke to execute each method call as a Callable on a single thread executor that delegates to an underlying NativeImpl.

    class SingleThreadAdapter {

        public SingleThreadAdapter(final String library) {
            this.executor = Executors.newSingleThreadExecutor();
            try {
                Executors.invoke(executor, new Runnable() {
                    public void run () { System.loadLibrary(library); }
                }, Boolean.TRUE);
            } catch (ExecutionException e) {
                // System.loadLibrary does not throw checked exceptions.
                Throwable cause = e.getCause();
                if (cause instanceof RuntimeException) {
                    throw (RuntimeException) cause;
                }
                else {
                    throw (Error) cause;
                }
            }
            catch (InterruptedException e) {
                throw new RuntimeException(e); // what should this really be?
            }
        }

        public <T> T newInstance(Callable<T> creator) throws Exception {
            try {
                T instance = Executors.invoke(executor, creator);
                return (T) Proxy.newProxyInstance(
                    instance.getClass().getClassLoader(),
                    instance.getClass().getInterfaces(),
                    new Handler(instance));
            } catch (ExecutionException e) {
                Throwable cause = e.getCause();
                if (cause instanceof Exception) {
                    throw (Exception) cause;
                }
                else {
                    throw (Error) cause;
                }
            }
            catch (InterruptedException e) {
                throw new RuntimeException(e); // what should this really be?
            }
        }

        private class Handler implements InvocationHandler {
            public Object invoke(Object proxy,
                                 final Method method,
                                 final Object[] args) throws Throwable {
                try {
                    return Executors.invoke(executor, new Callable<Object>() {
                        public Object call () throws Exception {
                            return method.invoke(instance, args);
                        }
                    });
                } catch (ExecutionException e) {
                    Throwable cause = e.getCause();
                    if (cause instanceof InvocationTargetException) {
                        throw cause.getCause();
                    }
                    else {
                        throw cause;
                    }
                }
                catch (InterruptedException e) {
                    throw new RuntimeException(e); // what should this really be?
                }
            }

            Handler(Object instance) {
                this.instance = instance;
            }

            private final Object instance;
        }

        private final Executor executor;
    }

Typical use of this class would be:

    try {
        SingleThreadAdapter adapter = new SingleThreadAdapter("LegacyEngine.dll");
        NativeInterface ni = (NativeInterface)
            adapter.newInstance(new Callable<NativeInterface>() {
                public NativeInterface call() {
                    return new NativeImpl(); // may use native calls
                }
            });
        int i = ni.nativeIntMethod();
    }
    // catch clauses for adapter creation, instance creation, invocation,
    // interruption...

Even though it is clear that SingleThreadExecutor and no other kind of Executor will be used in the implementation, there is no reason to expose this anywhere except in the SingleThreadAdapter constructor.

The exception handling is a bit tricky. Joe Bowbeer showed me how to do it right, but it took me a while to understand why it was right. I wonder if other users would be similarly confused.