Как написать прерываемые методы

#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. Как только процессы на одном уровне выполняются последовательно, не имеет большого значения, что они запускаются в разных местах. Я обновил свой ответ примером. Вы можете вложить процессы на желаемый уровень.