如何编写可中断的方法

时间:2010-07-05 10:21:06

标签: java multithreading interrupt

我有一种方法,在概念上看起来像:

Object f(Object o1) {
    Object o2 = longProcess1(o1);
    Object o3 = longProcess2(o2);
    return longProcess3(o3);
}

过程本身也可能是复合的:

Object longProcess1(Object o1) {
    Object o2 = longSubProcess1(o1);
    return longSubProcess2(o2);
}

等等,不同的过程可能存在于不同的模块中。大多数过程都很长,因为它们的计算成本很高,而不是IO限制。

到目前为止一切顺利,但现在我希望f作为一个整体可以打断。 The recommended Java way to do that将定期检查Thread.interrupted()的中断标记。它非常简单,但如果我需要将我的方法更改为以下内容,它很快就会变得很麻烦:

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);
}

...

现在,我确实理解这样工作的理性 - 它允许我更好地控制何时抛出InterruptedException(例如),避免让对象处于不一致状态 - 但我很想知道是否有一种更优雅的方式*

*在Java中,不是AspectJ,我认为这是非常合适的,但我坚持使用Java。

2 个答案:

答案 0 :(得分:7)

您可以使用界面和动态代理:

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);
    }

    ....
}

答案 1 :(得分:0)

我是否正确地按顺序运行处于相同嵌套级别的方法?如果是这样,为什么不将您的计算方法实现为java.lang.Runnable个实例,将它们组织成列表并在循环中启动它们?那么你只有一个Thread.interrupted()检查的地方。

您可以考虑使用java.util.concurrent.ExecutorService来促进对计算任务的控制。

更新了示例:

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();
                }
            }
        }
    }

}