[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.3 - (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 :     Response serve (Request req) throws ServiceException;
21 :     }
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.3 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 :     import java.util.concurrent.FutureTask;
42 :    
43 :     class NonBlockingServiceAdapter implements NonBlockingService {
44 :    
45 :     public NonBlockingServiceAdapter (BlockingService svc) {
46 :     this(svc, null);
47 :     }
48 :    
49 :     public NonBlockingServiceAdapter (BlockingService svc, Executor executor) {
50 :     this.blockingService = svc;
51 :     if (executor == null) {
52 :     this.executor = Executors.newFixedThreadPool(3);
53 :     }
54 :     else {
55 :     this.executor = executor;
56 :     }
57 :     }
58 :    
59 : tim 1.3 public Future&lt;Response&gt; serve (final Request req) {
60 :     Callable&lt;Response&gt; task = new Callable&lt;Response&gt;() {
61 : tim 1.1 public Response call () {
62 :     return blockingService.serve(req);
63 :     }
64 :     };
65 : tim 1.3 FutureTask&lt;Response&gt; ftask = new FutureTask&lt;Response&gt;(task);
66 : tim 1.1 executor.execute(ftask);
67 :     return ftask;
68 :     }
69 :    
70 :     private final BlockingService blockingService;
71 :     private final Executor executor;
72 :     }
73 :     </pre>
74 :    
75 :     <p> If I change my mind about using a single thread executor as the
76 :     default and want to use, say, a thread pool, I don't have to change
77 :     the imports and I don't have to learn any new types; I just
78 :     consult the <code>Executors</code> javadocs and change one line to something
79 :     like: </p>
80 :    
81 :     <pre>
82 :     this.executor = Executors.newCachedThreadPool();
83 :     </pre>
84 :    
85 :     <p> In this case it is irrelevant that the factory method returns a
86 :     more specific type. </p>
87 :    
88 :     <p> If there were no <code>Executor</code> factory methods or if those factory
89 :     methods were part of <code>AbstractExecutor/ThreadExecutor</code>, I'd have to
90 :     learn about and import a type just so I could get access to its
91 :     factory methods or public constructors. </p>
92 :    
93 :     <p> If I have more complicated requirements on the internal
94 :     executor, I can pass in an instance that has been configured in
95 :     advance with <code>Callbacks/Hooks/Intercepts</code>, possibly an extension of an
96 :     existing class or even my own custom implementation of <code>Executor</code>. </p>
97 :    
98 :     <p> If a later redesign requires lifecycle management for the
99 :     internal executor, under the current proposal I would change its
100 :     type to <code>ExecutorService</code> and provide methods on
101 :     <code>NonBlockingServiceAdapter</code> that ultimately call into the
102 :     <code>ExecutorService</code> instance. The executor and the executor
103 :     service are the same object under the current proposal, but this is not
104 :     a requirement for this example. </p>
105 :    
106 :    
107 :     <h2> Workaround Borland C++ DLL threading limitation </h2>
108 :    
109 :     <p> An annoying design flaw in one version of the Borland C++
110 :     (for Windows) runtime libraries causes a severe crash.if a C++
111 :     exception is thrown in DLL code within a thread other than the one
112 :     in which the DLL was loaded. For JNI users, there are several ways
113 :     around this problem, including changing the compiler and avoiding
114 :     the use of C++ exceptions. However, if none of these workarounds are
115 :     available, the only solution is to make sure that alls calls to native
116 :     methods implemented by such a DLL are made in the same thread as the
117 :     one that loaded the DLL. </p>
118 :    
119 :     <p> If you run into this situation repeatedly, as I did, you might
120 :     find yourself wanting an adapter class to do the heavy lifting for
121 :     you using a dynamic proxy, as follows: For a class <code>NativeImpl</code> with
122 :     native methods implemented by a Borland C++ DLL, define an interface
123 :     <code>NativeInterface</code> consisting of all of the native methods
124 :     of <code>NativeImpl</code>,
125 :     and make <code>NativeImpl</code> extend <code>NativeInterface</code>. The
126 :     adapter uses <code>Proxy.newProxyInstance</code> to create a new instance of this
127 :     interface by implementing the <code>invoke</code> to execute each method
128 :     call as a Callable on a single thread executor that delegates to an
129 :     underlying <code>NativeImpl</code>. </p>
130 :    
131 :     <pre>
132 : tim 1.2 class SingleThreadAdapter {
133 : tim 1.1
134 :     public SingleThreadAdapter (String library) {
135 :     this.executor = Executors.newSingleThreadExecutor();
136 :     try {
137 :     FutureTask ftask = new FutureTask(new Runnable () {
138 :     public void run () {
139 :     System.loadLibrary(library);
140 :     }
141 :     });
142 :     executor.executor(ftask);
143 :     }
144 :     catch (ExecutionException e) {
145 :     if (e.getCause() == null) {
146 :     throw new RuntimeException(e);
147 :     }
148 :     if (e.getCause() instanceof RuntimeException) {
149 :     throw (RuntimeException) e.getCause();
150 :     }
151 :     if (e.getCause() instanceof Error) {
152 :     throw (Error) e.getCause();
153 :     }
154 :     throw new RuntimeException(e.getCause());
155 :     }
156 :     // ... other invocation or interruption related catch clauses ...
157 :     }
158 :    
159 :     public Object newInstance (Callable creator) throws Exception {
160 :     try {
161 :     FutureTask createTask = new FutureTask(creator);
162 :     executor.execute(createTask);
163 :     Object instance = createTask.get();
164 :     return Proxy.newProxyInstance(
165 :     instance.getClass().getClassLoader(),
166 :     instance.getClass().getInterfaces(),
167 :     new Handler(instance));
168 :     }
169 :     catch (ExecutionException e) {
170 :     if (e.getCause() == null) {
171 :     throw e;
172 :     }
173 :     if (e.getCause() instanceof Exception) {
174 :     throw e.getCause();
175 :     }
176 :     else {
177 :     throw new RuntimeException(e.getCause());
178 :     }
179 :     }
180 :     // ... other invocation or interruption related catch clauses ...
181 :     }
182 :    
183 :     private class Handler implements InvocationHandler {
184 :     public Object invoke (Object proxy,
185 :     final Method method,
186 :     final Object[] args) throws Throwable {
187 :     try {
188 : tim 1.2 Callable task = new Callable () {
189 :     public Object call () throws Exception {
190 :     try {
191 :     return method.invoke(instance, args);
192 :     }
193 :     catch (Exception e) {
194 :     throw e;
195 :     }
196 :     catch (Throwable e) {
197 :     throw RuntimeException(e);
198 :     }
199 :     }
200 :     };
201 : tim 1.1 FutureTask ftask = new FutureTask(task)
202 :     executor.execute(ftask);
203 :     return ftask.get();
204 :     }
205 :     catch (ExecutionException e) {
206 :     throw e.getCause() == null ? e : e.getCause();
207 :     }
208 :     // ... other invocation or interruption related catch clauses ...
209 :     }
210 :    
211 :     Handler (Object instance) {
212 :     this.instance = instance;
213 :     }
214 :    
215 :     private final Object instance;
216 :     }
217 :    
218 :     private final Executor executor;
219 :     }
220 :     </pre>
221 :    
222 :     <p> Typical use of this class would be: </p>
223 :    
224 :     <pre>
225 :     try {
226 :     SingleThreadAdapter adapter = new SingleThreadAdapter("LegacyEngine.dll");
227 :     NativeInterface ni = (NativeInterface)
228 : tim 1.3 adapter.newInstance(new Callable&lt;NativeInterface&gt; () {
229 : tim 1.1 public NativeInterface call () {
230 : tim 1.2 return new NativeImpl(); // may use native calls
231 : tim 1.1 }
232 :     });
233 :     int i = ni.nativeIntMethod();
234 :     }
235 :     // catch clauses for adapter creation, instance creation, invocation,
236 :     // interruption...
237 :     </pre>
238 :    
239 :     <p> Even though it is clear that SingleThreadExecutor and no other kind
240 :     of Executor will be used in the implementation, there is no reason to
241 :     expose this anywhere except in the SingleThreadAdapter constructor. </p>
242 :    
243 :     <p> The exception handling is delicate and long winded. I punted on
244 :     most of it, and I'm not sure I got the rest right either. </p>
245 :    
246 :     </body>
247 :     </html>

Doug Lea
ViewVC Help
Powered by ViewVC 1.0.8