Parent Directory
|
Revision Log
Added missing type parameters to FutureTask. Fixed single thread adapter example and made sure it compiles.
<html>
<head><title> Executor examples </title></head>
<body>
<p> These probably don't qualify as canonical use cases, but they
are applications of executors that I have come across in real life.
</p>
<h2> Converting a blocking request into non-blocking request </h2>
<p> 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. </p>
<pre>
public interface Request ...
public interface Response ...
interface BlockingService {
Response serve(Request req) throws ServiceException;
}
</pre>
<p> I want an adapter that converts this interface into one that
returns a <code>Future<Response></code> without blocking. </p>
<pre>
interface NonBlockingService {
Future<Response> serve(Request req);
}
</pre>
<p> Here's how I might do it under the currently checked in
proposal. </p>
<pre>
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;
}
</pre>
<p> 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 <code>Executors</code> javadocs and change one line to something
like: </p>
<pre>
this.executor = Executors.newCachedThreadPool();
</pre>
<p> In this case it is irrelevant that the factory method returns a
more specific type. </p>
<p> If there were no <code>Executor</code> factory methods or if those factory
methods were part of <code>AbstractExecutor/ThreadExecutor</code>, I'd have to
learn about and import a type just so I could get access to its
factory methods or public constructors. </p>
<p> If I have more complicated requirements on the internal
executor, I can pass in an instance that has been configured in
advance with <code>Callbacks/Hooks/Intercepts</code>, possibly an extension of an
existing class or even my own custom implementation of <code>Executor</code>. </p>
<p> If a later redesign requires lifecycle management for the
internal executor, under the current proposal I would change its
type to <code>ExecutorService</code> and provide methods on
<code>NonBlockingServiceAdapter</code> that ultimately call into the
<code>ExecutorService</code> instance. The executor and the executor
service are the same object under the current proposal, but this is not
a requirement for this example. </p>
<h2> Workaround Borland C++ DLL threading limitation </h2>
<p> 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. </p>
<p> 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 <code>NativeImpl</code> with
native methods implemented by a Borland C++ DLL, define an interface
<code>NativeInterface</code> consisting of all of the native methods
of <code>NativeImpl</code>,
and make <code>NativeImpl</code> extend <code>NativeInterface</code>. The
adapter uses <code>Proxy.newProxyInstance</code> to create a new instance of this
interface by implementing the <code>invoke</code> to execute each method
call as a Callable on a single thread executor that delegates to an
underlying <code>NativeImpl</code>. </p>
<pre>
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;
}
</pre>
<p> Typical use of this class would be: </p>
<pre>
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...
</pre>
<p> 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. </p>
<p> The exception handling is a bit tricky. Joe Bowbeer showed me
how to do it right, but it took me a while to understand <em>why</em>
it was right. I wonder if other users would be similarly confused. </p>
</body>
</html>| Doug Lea | ViewVC Help |
| Powered by ViewVC 1.0.8 |