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<Response></code> without blocking. </p> |
26 |
|
27 |
<pre> |
28 |
interface NonBlockingService { |
29 |
Future<Response> 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<Response> serve(final Request req) { |
59 |
Callable<Response> task = new Callable<Response>() { |
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 <T> T newInstance(Callable<T> 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<Object>() { |
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<NativeInterface>() { |
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> |