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

Doug Lea
ViewVC Help
Powered by ViewVC 1.0.8