[cvs] / jsr166 / etc / notes / tim-executor-examples.html Repository:
ViewVC logotype

Annotation of /jsr166/etc/notes/tim-executor-examples.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.9 - (view) (download) (as text)

1 : tim 1.1 <html>
2 :     <head><title> Executor examples </title></head>
3 :     <body>
4 :    
5 :     <p> These probably don't qualify as canonical use cases, but they
6 :     are applications of executors that I have come across in real life.
7 :     </p>
8 :    
9 :     <h2> Converting a blocking request into non-blocking request </h2>
10 :    
11 :     <p> I have a service interface with a method that blocks, possibly
12 :     for a long time, for example, the client side of an HTTP-based
13 :     service that blocks while waiting for a response. </p>
14 :    
15 :     <pre>
16 :     public interface Request ...
17 :     public interface Response ...
18 :    
19 :     interface BlockingService {
20 : tim 1.6 Response serve(Request req) throws ServiceException;
21 : tim 1.1 }
22 :     </pre>
23 :    
24 :     <p> I want an adapter that converts this interface into one that
25 :     returns a <code>Future&lt;Response&gt;</code> without blocking. </p>
26 :    
27 :     <pre>
28 :     interface NonBlockingService {
29 : tim 1.6 Future&lt;Response&gt; serve(Request req);
30 : tim 1.1 }
31 :     </pre>
32 :    
33 :     <p> Here's how I might do it under the currently checked in
34 :     proposal. </p>
35 :    
36 :     <pre>
37 :     import java.util.concurrent.Callable;
38 :     import java.util.concurrent.Executor;
39 :     import java.util.concurrent.Executors;
40 :     import java.util.concurrent.Future;
41 :    
42 :     class NonBlockingServiceAdapter implements NonBlockingService {
43 :    
44 : tim 1.6 public NonBlockingServiceAdapter(BlockingService svc) {
45 : tim 1.1 this(svc, null);
46 :     }
47 :    
48 : tim 1.6 public NonBlockingServiceAdapter(BlockingService svc, Executor executor) {
49 : tim 1.1 this.blockingService = svc;
50 :     if (executor == null) {
51 :     this.executor = Executors.newFixedThreadPool(3);
52 :     }
53 :     else {
54 :     this.executor = executor;
55 :     }
56 :     }
57 :    
58 : tim 1.6 public Future&lt;Response&gt; serve(final Request req) {
59 : tim 1.3 Callable&lt;Response&gt; task = new Callable&lt;Response&gt;() {
60 : tim 1.6 public Response call() {
61 : tim 1.1 return blockingService.serve(req);
62 :     }
63 :     };
64 : tim 1.6 return Executors.execute(executor, task);
65 : tim 1.1 }
66 :    
67 :     private final BlockingService blockingService;
68 :     private final Executor executor;
69 :     }
70 :     </pre>
71 :    
72 : tim 1.4 <p> If I change my mind about using a fixed size thread pool as the
73 :     default and want to use, say, a caching thread pool, I don't have to change
74 : tim 1.1 the imports and I don't have to learn any new types; I just
75 :     consult the <code>Executors</code> javadocs and change one line to something
76 :     like: </p>
77 :    
78 :     <pre>
79 :     this.executor = Executors.newCachedThreadPool();
80 :     </pre>
81 :    
82 :     <p> In this case it is irrelevant that the factory method returns a
83 :     more specific type. </p>
84 :    
85 :     <p> If there were no <code>Executor</code> factory methods or if those factory
86 :     methods were part of <code>AbstractExecutor/ThreadExecutor</code>, I'd have to
87 :     learn about and import a type just so I could get access to its
88 :     factory methods or public constructors. </p>
89 :    
90 :     <p> If I have more complicated requirements on the internal
91 :     executor, I can pass in an instance that has been configured in
92 :     advance with <code>Callbacks/Hooks/Intercepts</code>, possibly an extension of an
93 :     existing class or even my own custom implementation of <code>Executor</code>. </p>
94 :    
95 :     <p> If a later redesign requires lifecycle management for the
96 :     internal executor, under the current proposal I would change its
97 :     type to <code>ExecutorService</code> and provide methods on
98 :     <code>NonBlockingServiceAdapter</code> that ultimately call into the
99 :     <code>ExecutorService</code> instance. The executor and the executor
100 :     service are the same object under the current proposal, but this is not
101 :     a requirement for this example. </p>
102 :    
103 :    
104 :     <h2> Workaround Borland C++ DLL threading limitation </h2>
105 :    
106 :     <p> An annoying design flaw in one version of the Borland C++
107 :     (for Windows) runtime libraries causes a severe crash.if a C++
108 :     exception is thrown in DLL code within a thread other than the one
109 :     in which the DLL was loaded. For JNI users, there are several ways
110 :     around this problem, including changing the compiler and avoiding
111 :     the use of C++ exceptions. However, if none of these workarounds are
112 :     available, the only solution is to make sure that alls calls to native
113 :     methods implemented by such a DLL are made in the same thread as the
114 :     one that loaded the DLL. </p>
115 :    
116 :     <p> If you run into this situation repeatedly, as I did, you might
117 :     find yourself wanting an adapter class to do the heavy lifting for
118 :     you using a dynamic proxy, as follows: For a class <code>NativeImpl</code> with
119 :     native methods implemented by a Borland C++ DLL, define an interface
120 :     <code>NativeInterface</code> consisting of all of the native methods
121 :     of <code>NativeImpl</code>,
122 :     and make <code>NativeImpl</code> extend <code>NativeInterface</code>. The
123 :     adapter uses <code>Proxy.newProxyInstance</code> to create a new instance of this
124 :     interface by implementing the <code>invoke</code> to execute each method
125 :     call as a Callable on a single thread executor that delegates to an
126 :     underlying <code>NativeImpl</code>. </p>
127 :    
128 :     <pre>
129 : tim 1.2 class SingleThreadAdapter {
130 : tim 1.1
131 : tim 1.8 public SingleThreadAdapter(final String library) {
132 : tim 1.1 this.executor = Executors.newSingleThreadExecutor();
133 :     try {
134 : tim 1.6 Executors.invoke(executor, new Runnable() {
135 :     public void run () { System.loadLibrary(library); }
136 :     }, Boolean.TRUE);
137 :     } catch (ExecutionException e) {
138 :     // System.loadLibrary does not throw checked exceptions.
139 : tim 1.8 Throwable cause = e.getCause();
140 :     if (cause instanceof RuntimeException) {
141 :     throw (RuntimeException) cause;
142 : tim 1.1 }
143 : tim 1.8 else {
144 :     throw (Error) cause;
145 :     }
146 :     }
147 :     catch (InterruptedException e) {
148 :     throw new RuntimeException(e); // what should this really be?
149 : tim 1.1 }
150 :     }
151 :    
152 : tim 1.8 public &lt;T&gt; T newInstance(Callable&lt;T&gt; creator) throws Exception {
153 : tim 1.1 try {
154 : tim 1.8 T instance = Executors.invoke(executor, creator);
155 :     return (T) Proxy.newProxyInstance(
156 : tim 1.1 instance.getClass().getClassLoader(),
157 :     instance.getClass().getInterfaces(),
158 :     new Handler(instance));
159 : tim 1.6 } catch (ExecutionException e) {
160 : tim 1.8 Throwable cause = e.getCause();
161 :     if (cause instanceof Exception) {
162 :     throw (Exception) cause;
163 :     }
164 :     else {
165 :     throw (Error) cause;
166 : tim 1.1 }
167 :     }
168 : tim 1.8 catch (InterruptedException e) {
169 :     throw new RuntimeException(e); // what should this really be?
170 :     }
171 : tim 1.1 }
172 :    
173 :     private class Handler implements InvocationHandler {
174 : tim 1.6 public Object invoke(Object proxy,
175 :     final Method method,
176 :     final Object[] args) throws Throwable {
177 : tim 1.1 try {
178 : tim 1.8 return Executors.invoke(executor, new Callable&lt;Object&gt;() {
179 : tim 1.2 public Object call () throws Exception {
180 : tim 1.8 return method.invoke(instance, args);
181 : tim 1.2 }
182 : tim 1.8 });
183 : tim 1.6 } catch (ExecutionException e) {
184 : tim 1.8 Throwable cause = e.getCause();
185 :     if (cause instanceof InvocationTargetException) {
186 :     throw cause.getCause();
187 :     }
188 :     else {
189 :     throw cause;
190 :     }
191 :     }
192 :     catch (InterruptedException e) {
193 :     throw new RuntimeException(e); // what should this really be?
194 : tim 1.1 }
195 :     }
196 :    
197 : tim 1.6 Handler(Object instance) {
198 : tim 1.1 this.instance = instance;
199 :     }
200 :    
201 :     private final Object instance;
202 :     }
203 :    
204 :     private final Executor executor;
205 :     }
206 :     </pre>
207 :    
208 :     <p> Typical use of this class would be: </p>
209 :    
210 :     <pre>
211 :     try {
212 :     SingleThreadAdapter adapter = new SingleThreadAdapter("LegacyEngine.dll");
213 :     NativeInterface ni = (NativeInterface)
214 : tim 1.6 adapter.newInstance(new Callable&lt;NativeInterface&gt;() {
215 :     public NativeInterface call() {
216 : tim 1.2 return new NativeImpl(); // may use native calls
217 : tim 1.1 }
218 :     });
219 :     int i = ni.nativeIntMethod();
220 :     }
221 :     // catch clauses for adapter creation, instance creation, invocation,
222 :     // interruption...
223 :     </pre>
224 :    
225 :     <p> Even though it is clear that SingleThreadExecutor and no other kind
226 :     of Executor will be used in the implementation, there is no reason to
227 :     expose this anywhere except in the SingleThreadAdapter constructor. </p>
228 :    
229 : tim 1.6 <p> The exception handling is a bit tricky. Joe Bowbeer showed me
230 :     how to do it right, but it took me a while to understand <em>why</em>
231 :     it was right. I wonder if other users would be similarly confused. </p>
232 : tim 1.1
233 :     </body>
234 :     </html>

Doug Lea
ViewVC Help
Powered by ViewVC 1.0.8