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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.8 - (show annotations)
Wed Jan 22 14:22:57 2003 UTC (17 years, 1 month ago) by tim
Branch: MAIN
CVS Tags: JSR166_CR1, JSR166_NOV3_FREEZE, JSR166_PRELIMINARY_TEST_RELEASE_1, JSR166_PRELIMINARY_TEST_RELEASE_2, JSR166_PFD, JSR166_DEC9_PRE_ES_SUBMIT, JSR166_PRERELEASE_0_1, JSR166_DEC9_POST_ES_SUBMIT
Changes since 1.7: +35 -26 lines
File MIME type: text/html
Added missing type parameters to FutureTask.
Fixed single thread adapter example and made sure it compiles.

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 Future&lt;Response&gt; serve(Request req);
30 }
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 public NonBlockingServiceAdapter(BlockingService svc) {
45 this(svc, null);
46 }
47
48 public NonBlockingServiceAdapter(BlockingService svc, Executor executor) {
49 this.blockingService = svc;
50 if (executor == null) {
51 this.executor = Executors.newFixedThreadPool(3);
52 }
53 else {
54 this.executor = executor;
55 }
56 }
57
58 public Future&lt;Response&gt; serve(final Request req) {
59 Callable&lt;Response&gt; task = new Callable&lt;Response&gt;() {
60 public Response call() {
61 return blockingService.serve(req);
62 }
63 };
64 return Executors.execute(executor, task);
65 }
66
67 private final BlockingService blockingService;
68 private final Executor executor;
69 }
70 </pre>
71
72 <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 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 class SingleThreadAdapter {
130
131 public SingleThreadAdapter(final String library) {
132 this.executor = Executors.newSingleThreadExecutor();
133 try {
134 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 Throwable cause = e.getCause();
140 if (cause instanceof RuntimeException) {
141 throw (RuntimeException) cause;
142 }
143 else {
144 throw (Error) cause;
145 }
146 }
147 catch (InterruptedException e) {
148 throw new RuntimeException(e); // what should this really be?
149 }
150 }
151
152 public &lt;T&gt; T newInstance(Callable&lt;T&gt; creator) throws Exception {
153 try {
154 T instance = Executors.invoke(executor, creator);
155 return (T) Proxy.newProxyInstance(
156 instance.getClass().getClassLoader(),
157 instance.getClass().getInterfaces(),
158 new Handler(instance));
159 } catch (ExecutionException e) {
160 Throwable cause = e.getCause();
161 if (cause instanceof Exception) {
162 throw (Exception) cause;
163 }
164 else {
165 throw (Error) cause;
166 }
167 }
168 catch (InterruptedException e) {
169 throw new RuntimeException(e); // what should this really be?
170 }
171 }
172
173 private class Handler implements InvocationHandler {
174 public Object invoke(Object proxy,
175 final Method method,
176 final Object[] args) throws Throwable {
177 try {
178 return Executors.invoke(executor, new Callable&lt;Object&gt;() {
179 public Object call () throws Exception {
180 return method.invoke(instance, args);
181 }
182 });
183 } catch (ExecutionException e) {
184 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 }
195 }
196
197 Handler(Object instance) {
198 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 adapter.newInstance(new Callable&lt;NativeInterface&gt;() {
215 public NativeInterface call() {
216 return new NativeImpl(); // may use native calls
217 }
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 <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
233 </body>
234 </html>

dl@cs.oswego.edu
ViewVC Help
Powered by ViewVC 1.1.27