2014-02-12 4 views
7

У меня есть поток Java с методом run, который вычисляет много вещей. Вы можете думать об этом как о серии математических утверждений следующим образом. Обратите внимание, что в каждом вычислении могут использоваться другие методы, которые, в свою очередь, могут иметь дополнительные циклы и т. Д.Java - завершение метода в потоке

public void run() { 
    [computation 1 goes here here that takes a few seconds] 
    [computation 2 goes here that takes a few seconds] 
    .... 
    [computation 30 goes here that takes a few seconds] 
} 

Существует GUI, который печатает вывод этих заявлений, поскольку они производят свои результаты, и я хотел бы, чтобы пользователь мог сказать «Стоп», когда они хотят. Вот два метода я думал о


Метод 1: Многие Boolean Проверяет [Смотрит СТРАШНО]

private boolean stop; 
public void run() { 
    if(!stop) 
     [computation 1 goes here here that takes a few seconds] 
    if(!stop) 
     [computation 2 goes here that takes a few seconds] 
    .... 
    if(!stop) 
     [computation 30 goes here that takes a few seconds] 
} 

В этом методе, когда флаг остановки был установлен верно, вычисления будут конец. Обратите внимание, как это выглядит глупо, имея 30 if заявлений. Важно отметить, что критический вопрос заключается в том, как часто устанавливать эти условия. Обратите внимание, что вычисления на каждой строке не обязательно являются одной строкой. Взято до крайности, делает КАЖДУЮ строку в методе run() заслуживает того, чтобы позвонить над ним над номером if(!stop)? Это не похоже на хороший дизайн.

Способ 2: Обобщая вычисление [НЕ МОЖЕТ СДЕЛАТЬ ЭТО]

pirivate boolean stop; 
public void run() { 
    for(int i = 0; i < computationsN && !stop; i++) { 
     computuations.get(i).compute(); 
} 

Я полагаю, что этот метод будет предложен, поэтому я хотел бы просто сказать, что это невозможно в моем случае, учитывая явные вариации в строках, которые я называю «вычислениями» для простоты. Обычно я делал это для потоков, которые являются базовыми циклами while, и он отлично подходит для таких целей. Но не в этом случае, когда метод run() является просто огромным методом переменного кода.


Какие-либо другие решения там? Похоже, это должна быть универсальная проблема. Заранее спасибо!

+0

Catch InterruptedException, бросить прерывание в потоке ...? – keshlam

+0

Бросить прерывание на поток? что вы имеете в виду? – CodeGuy

+0

Поначалу кажется, что (по крайней мере), метод 1 может быть преувеличен, помещая вычисления в общий класс и набивая их в массив. Перейдите через массив и проверьте флаг остановки после каждого вычисления. Редактировать: nevermind! Я просто понял, что это ваш метод 2. Сожалею! :) Но это выглядит актуальным: http://stackoverflow.com/questions/5915156/how-can-i-kill-a-thread-without-using-stop – Nathan

ответ

5

что вы хотите сделать на самом деле, можно сделать с помощью метода 2, но вы должны использовать Strategy Pattern, это действительно еще одна вещь, которая вам нужна, поскольку она позволяет упростить ваши вычисления в одной строке, например, вы Фактически это было сделано в методе 2.

Он работает следующим образом: он позволяет вам изменить следующий исполняемый алгоритм, выполнив полиморфизм.

enter image description here

Итак, сначала вы должны сделать все ваши алгоритмы в разных классах, и каждый из них имеет реализовать один интерфейс (это можно было бы назвать Computable) с одним методом, то есть, ваш метод compute().

Ex.

public interface Computable { 
    public void compute(); 
} 

И ваши классы Алгоритмы может быть что-то вроде:

public class AlgorithmX implements Computable { 

    @Override 
    public void compute() { 
     // TODO Your Real Computation here for the Algorithm X 
    } 
    } 

Тогда в вашем for Loop ваш computations Collection (или массив) заполняется с объектами, которые реализуют Computable, то есть, с вашими объектами алгоритмов.

for(int i = 0; i < computations && !stop; i++) { 
    computuations.get(i).compute(); 
} 

Итак, вы находитесь на правильном пути с помощью метода 2, я надеюсь, что теперь ваш путь станет более ясным.

Cheers!

+0

Спасибо за ваш ответ, однако я понимаю, что это просто более удобный способ сделать метод 2.Но, уверяю вас, между моими «вычислительными» строками мало сходства. Я просто назвал их вычислениями для простоты вопроса ... Я не думаю, что группировка их в этом отношении логична в моем случае. – CodeGuy

+0

CodeGuy, они не обязательно должны быть похожими вычислениями для использования этого шаблона. Конструкторы для различных классов могут принимать разные аргументы. Можете ли вы привести один пример вычислений 1 и 2, которые не позволили бы использовать этот шаблон? – dewald

+0

Не имеет значения, насколько похожи или сходны линии вычислений. используя «метод 2», он абстрагирует их на вызываемые «вещи». если вы не хотите этого делать, вам понадобятся все эти утверждения if. там действительно нет других вариантов. есть много преимуществ для абстрагирования этих вычислений с их собственными классами. вы можете применить другие стандартные функции, например, подсчитать, сколько из них было выполнено. применяя стандартные механизмы отказа и т. д., вы можете загружать их в очереди и обрабатывать их по-разному. – slipperyseal

1

Вместо использования stop флаг можно назвать interrupt() на резьбе, чтобы остановить его, и в чеке run метод, чтобы увидеть if (Thread.interrupted()) истинно (interrupt() не сразу остановить поток, вы все еще есть, чтобы проверить Thread.interrupted()). Таким образом, вы избегаете ловушек, таких как забывание объявить свой флаг как volatile.

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#interrupt%28%29

Краткого герметизирующие все ваши вычислений в Runnables затем положить их в массиве и зацикливание на них, которые вы исключили, метод 1 является лучшим выбором. Что касается того, как часто вы должны проверять, прерван ли ваш поток, зависит от того, сколько времени потребуется на выполнение ваших вычислений, и насколько оперативно вы хотите, чтобы ваша программа была в команде Stop, вы можете добавить несколько операторов println(System.currentTimeMillis), чтобы получить представление времени вычислений, затем добавьте if(Thread.interrupted()) return; каждые 500 миллисекунд или около того, чтобы остановить метод run.

я бы сразу исключил метод 2, однако, как ваши расчеты не должны иметь ничего общего для того, чтобы вы, чтобы поместить их в Runnables

private ArrayList<Runnable> runnables = new ArrayList<>(); 

runnables.add(new Runnable() { 
    public void run() { 
    // computation 1 
    } 
}) 

runnables.add(new Runnable() { 
    public void run() { 
    // computation 2 
    } 
}) 

// etc 

public void run() { 
    for(Runnable runnable: runnables) { 
    if(Thread.interrupted()) return; 
    runnable.run(); 
    } 
} 
0

В зависимости от того, как графический интерфейс структурированный, есть ли вероятность, что вы могли бы выбросить исключение (возможно, прерванное исключение) из кода GUI, который выдул бы длинный процесс?

Ключевое значение - найти где-то в вашем потоке кода, где есть узкое место в точке, где может быть вставлена ​​чек. Это может быть не очень очевидным (например, вы можете зацепить менеджера перерисовки в приложении swing) - и это, безусловно, не очень хорошая практика в общем случае, но для очень конкретного случая использования это может быть уместно.

Если это не возможно, чуть менее многословным подход будет использовать метод как это:

assertContinue() throws InterruptedException; 

(или, возможно, назвать его переменного тока(), чтобы сохранить ввод вниз).

вы все еще закручиваетесь в своем алгоритме, но, по крайней мере, это меньше печатает.

О, и что это за расчеты? Блокируют ли они блокировку ввода/вывода или блокировки? Если это так, то вызов Thread.interrupt() будет работать ...

+0

Они не блокируют вообще. Я просто не хочу ставить все вокруг, это выглядит ужасно. Думаю, мне все равно придется это делать. Благодаря! – CodeGuy

+0

@CodeGuy Как структурирован ваш код GUI? Не могли бы вы поместить чек в вызовы графического интерфейса? Это может быть хорошим узким местом. –

Смежные вопросы