How to write interruptable methods
I have a method which, conceptually, looks something like:
Object f(Object o1) {
Object o2 = longProcess1(o1);
Object o3 = longProcess2(o2);
return longProcess3(o3);
}
Where the processes themselves might also be compound:
Object longProcess1(Object o1) {
Object o2 = longSubProcess1(o1);
return longSubProcess2(o2);
}
And so forth, with the different processes potentially sitting in different modules. Most of the processes are long because they are computationally expensive, not IO-bound.
So far so good, but now I want f
as a whole to be interruptable. The recommended Java way to do that is to periodically check for the interrupted flag with Thread.interrupted()
. It's pretty straightforward, but it can quickly become cumbersome if I need to change my methods to something like:
Object f(Object o1) {
Object o2 = longProcess1(o1);
if (Thread.interrupted()) throw new InterruptedException();
Object o3 = longProcess2(o2);
if (Thread.interrupted()) throw new InterruptedException();
return longProcess3(o3);
}
Object longProcess1(Object o1) {
Object o2 = longSubProcess1(o1);
if (Thread.interrupted()) throw new InterruptedException();
return longSubProcess2(o2);
}
...
Now, I do understand the rational for working l开发者_运维问答ike that - it allows me to better control when the InterruptedException (for example) will be thrown, avoiding leaving objects in inconsistent states - but I am curious to know if there's a more elegant way of doing that*.
* In Java, not AspectJ, which I guess is very appropriate here but I'm stuck with Java.
You could use an interface and a Dynamic proxy:
public class Wrapper {
public static <T> T wrap(Class<T> intf, final T impl) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Object proxy = Proxy.newProxyInstance(cl, new Class<?>[] {intf},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (Thread.interrupted()) {
throw new InterruptedException();
}
return method.invoke(impl, args);
}
});
return intf.cast(proxy);
}
}
interface Processes {
Object longProcess1(Object o);
...
}
public class ProcessesImpl implement Processes {
Processes self = Wrapper.wrap(Processes.class, this);
public Object f(Object o1) {
Object o2 = self.longProcess1(o1);
Object o3 = self.longProcess2(o2);
return self.longProcess3(o3);
}
public Object longProcess1(Object o1) {
Object o2 = self.longSubProcess1(o1);
return self.longSubProcess2(o2);
}
....
}
Did I get this right that you sequentially run methods that are at the same nesting level? If so, why not just implement your computation methods as java.lang.Runnable
instances, organize them into lists and start them in the loop? Then you would have only one place with Thread.interrupted()
check.
You might consider using java.util.concurrent.ExecutorService
to facilitate the control over computation tasks.
Updated with an example:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<CompoundProcess> subProcesses1 = new ArrayList<CompoundProcess>();
subProcesses1.add(new CompoundProcess() {
public void run() {
System.out.println("Process 1.1");
}
});
subProcesses1.add(new CompoundProcess() {
public void run() {
System.out.println("Process 1.2");
}
});
List<CompoundProcess> subProcesses2 = new ArrayList<CompoundProcess>();
subProcesses2.add(new CompoundProcess() {
public void run() {
System.out.println("Process 2.1");
}
});
subProcesses2.add(new CompoundProcess() {
public void run() {
System.out.println("Process 2.2");
}
});
List<CompoundProcess> processes1 = new ArrayList<CompoundProcess>() {};
processes1.add(new CompoundProcess(subProcesses1));
processes1.add(new CompoundProcess(subProcesses2));
CompoundProcess process = new CompoundProcess(processes1);
process.run();
}
static class CompoundProcess implements Runnable {
private List<CompoundProcess> processes = new ArrayList<CompoundProcess>();
public CompoundProcess() {
}
public CompoundProcess(List<CompoundProcess> processes) {
this.processes = processes;
}
public void run() {
for (Runnable process : processes) {
if (Thread.interrupted()) {
throw new RuntimeException("The processing was interrupted");
} else {
process.run();
}
}
}
}
}
精彩评论