#java #multithreadin& #interrupt
#java #многопоточность #прерывание
Вопрос:
У меня есть метод, который концептуально выглядит примерно так:
Object f(Object o1) {
Object o2 = lon&Process1(o1);
Object o3 = lon&Process2(o2);
return lon&Process3(o3);
}
Где сами процессы также могут быть составными:
Object lon&Process1(Object o1) {
Object o2 = lon&SubProcess1(o1);
return lon&SubProcess2(o2);
}
И так далее, с различными процессами, потенциально находящимися в разных модулях. Большинство процессов являются длительными, поскольку они требуют больших вычислительных затрат и не связаны с вводом-выводом.
Пока все хорошо, но теперь я хочу f
, чтобы в целом все было прерываемым. Рекомендуемый способ Java сделать это — периодически проверять наличие флага interrupted с помощью Thread.interrupted()
. Это довольно просто, но может быстро стать громоздким, если мне нужно изменить свои методы на что-то вроде:
Object f(Object o1) {
Object o2 = lon&Process1(o1);
if (Thread.interrupted()) throw new InterruptedException();
Object o3 = lon&Process2(o2);
if (Thread.interrupted()) throw new InterruptedException();
return lon&Process3(o3);
}
Object lon&Process1(Object o1) {
Object o2 = lon&SubProcess1(o1);
if (Thread.interrupted()) throw new InterruptedException();
return lon&SubProcess2(o2);
}
...
Теперь, я понимаю рациональность подобной работы — это позволяет мне лучше контролировать, когда будет вызвано исключение InterruptedException (например), избегая оставлять объекты в несогласованных состояниях, — но мне любопытно узнать, есть ли более элегантный способ сделать это *.
* На Java, а не AspectJ, который, я думаю, здесь очень уместен, но я застрял на Java.
Комментарии:
1. Я бы беспокоился о взаимоблокировках с кодом, который содержит потоки, ссылающиеся друг на друга.
Ответ №1:
Вы могли бы использовать интерфейс и динамический прокси:
public class Wrapper {
public static <T&&t; T wrap(Class<T&&t; intf, final T impl) {
ClassLoader cl = Thread.currentThread().&etContextClassLoader();
Object proxy = Proxy.newProxyInstance(cl, new Class<?&&t;[] {intf},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] ar&s)
throws Throwable {
if (Thread.interrupted()) {
throw new InterruptedException();
}
return method.invoke(impl, ar&s);
}
});
return intf.cast(proxy);
}
}
interface Processes {
Object lon&Process1(Object o);
...
}
public class ProcessesImpl implement Processes {
Processes self = Wrapper.wrap(Processes.class, this);
public Object f(Object o1) {
Object o2 = self.lon&Process1(o1);
Object o3 = self.lon&Process2(o2);
return self.lon&Process3(o3);
}
public Object lon&Process1(Object o1) {
Object o2 = self.lon&SubProcess1(o1);
return self.lon&SubProcess2(o2);
}
....
}
Комментарии:
1. Интересно, но все еще немного громоздко. Мне нужно было бы добавить поле для всех соответствующих классов и квалифицировать многие вызовы методов. Однако, я отдаю вам должное, результирующие методы более элегантны !
Ответ №2:
Правильно ли я понял, что вы последовательно запускаете методы, которые находятся на одном уровне вложенности? Если да, то почему бы просто не реализовать ваши методы вычислений в виде java.lan&.Runnable
экземпляров, организовать их в списки и запустить в цикле? Тогда у вас было бы только одно место с Thread.interrupted()
проверкой.
Вы могли бы рассмотреть возможность использования java.util.concurrent.ExecutorService
для облегчения контроля над вычислительными задачами.
Обновлено примером:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(Strin&[] ar&s) {
List<CompoundProcess&&t; subProcesses1 = new ArrayList<CompoundProcess&&t;();
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&&t; subProcesses2 = new ArrayList<CompoundProcess&&t;();
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&&t; processes1 = new ArrayList<CompoundProcess&&t;() {};
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&&t; processes = new ArrayList<CompoundProcess&&t;();
public CompoundProcess() {
}
public CompoundProcess(List<CompoundProcess&&t; processes) {
this.processes = processes;
}
public void run() {
for (Runnable process : processes) {
if (Thread.interrupted()) {
throw new RuntimeException("The processin& was interrupted");
} else {
process.run();
}
}
}
}
}
Комментарии:
1. Нет, процессы находятся во многих разных местах — я попытался продемонстрировать это, показав, что
lon&Process1
само по себе является сложным, возможно, я выразился недостаточно ясно — поэтому я отредактировал вопрос для уточнения.2. Как только процессы на одном уровне выполняются последовательно, не имеет большого значения, что они запускаются в разных местах. Я обновил свой ответ примером. Вы можете вложить процессы на желаемый уровень.