Java: запустить Callable в отдельном процессе - PullRequest
2 голосов
/ 05 декабря 2008

Учитывая экземпляр x из Callable<T>, как я могу запустить x в отдельном процессе, чтобы я мог перенаправить стандартный ввод и вывод процесса? Например, есть ли способ построить Process из Callable? Существует ли стандарт Executor, который дает контроль над входом и выходом?

[ОБНОВЛЕНИЕ] Не важно, чтобы Callable выполнялся в новом процессе, а не в новом потоке. Я хочу поместить экземпляр Callable в "жгут", чтобы я мог контролировать его stdin / stdout. AFAIK, это требует нового процесса.

Ответы [ 4 ]

2 голосов
/ 05 декабря 2008

В целом:

Учитывая экземпляр x Callable, использующий глобальные переменные A и B, как я могу запустить x одновременно, так что x видит пользовательские значения для A и B, а не «исходные» значения A и B?

И лучший ответ: не используйте глобальные переменные. Зависимости вводят такие вещи. Расширьте Callable и добавьте методы setStdIn, setStdOut (и setStdErr, если вам это нужно).

Я знаю, что это не тот ответ, который вы ищете, но все решения, которые я видел для этого, требуют нового процесса, и единственный способ получить Callable в новом процессе - это изменить код Callable, так что он сериализуем, или предоставляет имя класса, или какой-то другой взлом, поэтому вместо того, чтобы вносить изменения, которые дадут вам неприятное, хрупкое решение, просто сделайте это правильно *

* «правильно» использовать широко распространенный шаблон внедрения зависимостей для обеспечения слабой связи. YMMV

ОБНОВЛЕНИЕ: В ответ на комментарий 1. Вот ваш новый интерфейс:

import java.io.InputStream;
import java.io.PrintStream;
import java.util.concurrent.Callable;


public interface MyCallable<V> extends Callable<V> {
  void setStdIn(InputStream in);
  void setStdOut(PrintStream out);  
}

и ваши задачи будут выглядеть так:

import java.io.InputStream;
import java.io.PrintStream;


public class CallableTask implements MyCallable<Object> {

    private InputStream in = System.in;
    private PrintStream out = System.out;

    public void setStdIn(InputStream in) {
        this.in = in;
    }

    public void setStdOut(PrintStream out) {
        this.out = out;
    }

    public Object call() throws Exception {
        out.write(in.read());
        return null;
    }

}

Нет необходимости в процессе. Любое решение (даже одно, использующее процессы) почти наверняка потребует каких-либо изменений кода для Callables. Это самое простое (просто замените System.out на this.out).

1 голос
/ 05 декабря 2008

Стремитесь структурировать свой код, используя параметризацию сверху, внедрение зависимостей или как вы хотите это называть. Избегайте статики, даже если она одета как одиночка или спрятана в плохой библиотеке.

Если вы не хотите отменять использование System.in/out/err, вам повезло. Они могут быть установлены глобально с System.set(In/Out/Err). Чтобы иметь возможность потоковой передачи по-разному для отдельных потоков, установите реализацию, которая использует ThreadLocal, чтобы найти цель для делегирования. Местные жители, как правило, злые, но могут быть полезны в неудачных ситуациях.

Из пояснений видно, что первоначальному постеру нет необходимости создавать отдельный процесс.

1 голос
/ 05 декабря 2008

stdin / stdout может быть перенаправлен для всей системы, но я не уверен, что он может быть перенаправлен для одного потока - вы всегда просто получаете его из System. (Вы можете заставить System.out переходить в другой поток, но я использовал его для перехвата трассировки стека до того, как появился метод для получения трассировки в виде строки)

Вы можете перенаправить stdin / stdout, запустить вызываемый модуль и затем перенаправить его обратно, но если что-то еще использует System.out, пока оно перенаправлено, это также перейдет в ваш новый файл.

Методами будут System.setIn () и System.setOut () (и System.setErr ()), если вы хотите пойти по этому пути.

0 голосов
/ 05 декабря 2008

Посмотрите на класс ProcessBuilder, он дает вам возможность захватить stdout и stderr процесса, который он запускает.

Вы можете создать класс Main, который запускает Callable, а затем запустить его как другой jvm с ProcessBuilder. Класс Main может принять имя класса вашего вызываемого объекта в качестве входного параметра командной строки и загрузить его с помощью Class.forName () и Class.newInstance ().

Альтернативой, если вы хотите, чтобы он запускал конкретный вызываемый экземпляр, является сериализация экземпляра в файл перед запуском другого процесса (это было бы очень грубой формой взаимодействия между двумя процессами).

Вопрос в том, почему вы хотите это сделать? ты не можешь просто запустить свой вызываемый в другой теме? java.util.concurrent имеет несколько полезных пулов потоков, просто для этого.

...