2008-09-18 2 views
28

Вот моя проблема: у меня есть диалог с некоторыми параметрами, которые пользователь может изменить (например, с помощью счетчика). Каждый раз, когда один из этих параметров изменяется, я запускаю поток для обновления 3D-представления в соответствии с новым значением параметра. Если пользователь меняет другое значение (или одно и то же значение снова, многократно нажимая стрелку), когда первый поток работает, я хотел бы прервать первый поток (и обновление 3D-вида) и запустить новый один с последним значением параметра.Как прервать поток быстрым и чистым способом в java?

Как я могу сделать что-то подобное?

PS: В моем методе run() нет цикла, поэтому проверка флажка не является вариантом: поток, обновляющий трехмерный вид, в основном вызывает только один метод, который очень длинный для выполнения. Я не могу добавить какой-либо флаг в этот метод, требуя прервать либо, так как у меня нет доступа к его коду.

+0

Можете ли вы дать нам больше информации о API, который вы используете? Вы говорите, что вы передаете что-то определенному методу из сторонней библиотеки, и это занимает много времени. Этот метод напрямую изменяет объект, в который вы проходите? Или вы получаете обратный вызов с результатами? – 2008-09-18 16:20:52

+0

Завершает ли эта нить нормально до завершения вашего приложения? Если да, то что заставляет его остановиться? – 2008-09-18 16:28:31

ответ

12

Попробуйте прерывать(), как говорили некоторые, чтобы определить, имеет ли значение ваш поток.Если нет, попробуйте уничтожить или закрыть ресурс, который остановит поток. У этого есть шанс быть немного лучше, чем пытаться бросить Thread.stop() на нем.

Если производительность допустима, вы можете просмотреть каждое трехмерное обновление в виде дискретного события без прерывания и просто допустить его до конца, после чего проверить, будет ли выполнено новое последнее обновление. Это может сделать GUI немного изменчивым для пользователей, так как они смогут сделать пять изменений, а затем увидеть графические результаты от того, как изменилось пять изменений назад, а затем увидеть результат их последнего изменения. Но в зависимости от того, как долго этот процесс, он может быть терпимым, и это позволит избежать необходимости уничтожать поток. Дизайн может выглядеть следующим образом:

boolean stopFlag = false; 
Object[] latestArgs = null; 

public void run() { 
    while (!stopFlag) { 
    if (latestArgs != null) { 
     Object[] args = latestArgs; 
     latestArgs = null; 
     perform3dUpdate(args); 
    } else { 
     Thread.sleep(500); 
    } 
    } 
} 

public void endThread() { 
    stopFlag = true; 
} 

public void updateSettings(Object[] args) { 
    latestArgs = args; 
} 
8

Нить, обновляющая трехмерный вид, должна периодически проверять некоторый флаг (используйте volatile boolean), чтобы увидеть, следует ли его прекратить. Когда вы хотите прервать поток, просто установите флаг. Когда поток затем проверяет флаг, он должен просто вырваться из любого цикла, который он использует, чтобы обновить представление и вернуться из своего метода run.

Если вы действительно не можете получить доступ к коду, который работает Thread, чтобы проверить флажок, тогда нет безопасного способа остановить поток. Всегда ли эта тема заканчивается, прежде чем ваше приложение завершится? Если да, то что заставляет его остановиться?

Если он работает в течение некоторого длительного периода времени, и вы просто должны его закончить, вы можете использовать устаревший метод Thread.stop(). Однако это было устарело по уважительной причине. Если этот поток остановлен в середине некоторой операции, которая оставляет что-то в несогласованном состоянии или какой-то ресурс не очищается должным образом, тогда у вас могут быть проблемы. Вот примечание от documentation:

Этот метод по своей сути является небезопасным. Остановка нити с Thread.stop заставляет его разблокировать все мониторов, которые он запертые (как естественного следствия неконтролируемого исключения ThreadDeath распространяющегося вверх стеки). Если какой-либо из объектов , ранее защищенных этими мониторами , находился в противоречивом состоянии, поврежденные объекты становятся видимыми для других потоков, потенциально приводящих к в произвольном поведении. Множество применений остановки следует заменить кодом, который просто изменяет некоторую переменную до , указывая на то, что целевой поток должен остановить работу. Целевая нить должна регулярно проверять эту переменную, а следует возвращать из метода запуска в порядке , если переменная указывает, что она должна прекратиться. Если целевой поток ожидает длинных периодов (для переменной условия, для примера ), для прерывания ожидания используется метод прерывания . Для дополнительной информации см Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

+0

Протектор, обновляющий трехмерный вид, в основном вызывает только один метод, который очень длинный для выполнения. Я не могу добавить какой-либо флаг в этот метод, поскольку у меня нет доступа к его коду. Поэтому это решение не может быть использовано здесь. – jumar 2008-09-18 16:16:04

+0

Решение, предложенное Дейвом Л., является правильным. Если у вас нет доступа к исходному коду стороннего кода, возможно, единственное, что у вас осталось, - это байт-код. Используя библиотеку, такую ​​как ASM (http://asm.objectweb.org/), вы можете изменить класс во время выполнения и «ввести» свой собственный код. – 2008-09-18 18:19:35

-1

Может быть, это может помочь вам: How can we kill a running thread in Java?

Вы можете убить определенную нить, установив переменную внешнего класса.

Class Outer 
{  
    public static flag=true; 
    Outer() 
    { 
     new Test().start(); 
    } 
    class Test extends Thread 
    {    
     public void run() 
     { 
     while(Outer.flag) 
     { 
      //do your work here 
     } 
     } 
    } 
    } 

, если вы хотите, чтобы остановить выше нить, установите переменный флаг false. Другой способ убить поток - просто зарегистрировать его в ThreadGroup, затем вызвать destroy(). Этот способ также можно использовать для уничтожения подобных потоков, создавая их как группу или регистрируясь с группой.

+4

Если вы хотите, чтобы люди серьезно относились к вам, прекратите использовать «u» и «ur». Потратьте время, чтобы набрать все лишние 2 символа - это не задержит вас так долго. – 2008-10-30 01:21:16

1

Путь я реализовал что-то подобное в прошлом реализовать метод shutdown() в моем Runnable подкласса, который устанавливает переменную экземпляра с именем should_shutdown истина. Метод run() обычно выполняет что-то в цикле и периодически проверяет should_shutdown и когда он верен, возвращает или вызывает do_shutdown(), а затем возвращается.

Вы должны сохранить ссылку на текущий рабочий поток, а когда пользователь изменит значение, вызовите shutdown() в текущем потоке и дождитесь его завершения. Затем вы можете запустить новый поток.

Я бы не рекомендовал использовать Thread.stop, поскольку он был устаревшим в прошлый раз, когда я проверил.

Edit:

Прочитайте комментарий о том, как ваш рабочий поток просто вызывает другой метод, который занимает некоторое время, чтобы работать, поэтому выше не применяется. В этом случае ваши единственные реальные варианты - попробовать позвонить interrupt() и посмотреть, не имеет ли эффекта. Если нет, подумайте о том, как вручную вызывается функция, которую ваш рабочий поток вызывает для разрыва. Например, похоже, что он выполняет сложный рендеринг, поэтому, возможно, уничтожает холст и вызывает его исключение. Это нехорошее решение, но, насколько я могу судить, это единственный способ остановить поток в подобной ситуации.

1

Поток будет завершен после того, как его метод run() будет завершен, поэтому вам потребуется проверка, которая заставит ее закончить метод.

Вы можете прервать нить, а затем провести проверку, которая будет периодически проверять isInterrupted() и возвращать из метода run().

Вы также можете использовать логическое значение, которое периодически проверяется в потоке и заставляет его возвращать, если это так, или помещать поток внутри цикла, если он выполняет некоторую повторяющуюся задачу, и затем он выйдет из метода run(), когда вы установите логическое значение. Например,

static boolean shouldExit = false; 
Thread t = new Thread(new Runnable() { 
    public void run() { 
     while (!shouldExit) { 
      // do stuff 
     } 
    } 
}).start(); 
1

К сожалению, убийство нити по своей сути небезопасен из-за возможности использования ресурсов, которые могут быть синхронизированы с помощью замков и, если нить вы убиваете в настоящее время имеет блокировку, может привести к программе, входящей в тупик (постоянную попытка захватить ресурс, который не может быть получен). Вам придется вручную проверить, нужно ли его убить из потока, который вы хотите остановить. Volatile будет проверять истинное значение переменной, а не то, что могло быть сохранено ранее. На боковой ноте Thread.join в выходящем потоке, чтобы вы дождались, пока умирающий поток не исчезнет, ​​прежде чем что-то делать, а не проверять все время.

4

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

Внешняя резьба:

if(oldThread.isRunning()) 
{ 
    oldThread.interrupt(); 
    // Be careful if you're doing this in response to a user 
    // action on the Event Thread 
    // Blocking the Event Dispatch Thread in Java is BAD BAD BAD 
    oldThread.join(); 
} 

oldThread = new Thread(someRunnable); 
oldThread.start(); 

Внутренняя Runnable/Автор:

public void run() 
{ 
    // If this is all you're doing, interrupts and boolean flags may not work 
    callExternalMethod(args); 
} 

public void run() 
{ 
    while(!Thread.currentThread().isInterrupted) 
    { 
     // If you have multiple steps in here, check interrupted peridically and 
     // abort the while loop cleanly 
    } 
} 
+0

Чтобы ответить на ваш вопрос, это связано с тем, что Thread.intrerrupt() может вызывать побочные эффекты, бросая исключения во время определенных операций ввода-вывода или параллелизма. Иногда это именно то, что вы хотите, но не всегда. – 2008-09-18 16:38:45

0

Так как вы имеете дело с кодом вы не имеете доступа к вы, вероятно, не повезло. Стандартная процедура (как указано в других ответах) заключается в наличии флажка, который периодически проверяется текущим потоком. Если флаг установлен, выполните очистку и завершение.

Поскольку эта опция недоступна для вас, единственным вариантом является принудительное завершение работы. Раньше это возможно путем вызова Thread.stop(), но этот метод был окончательно устаревшим по следующей причине (копируется из Javadocs):

Этот метод является по своей сути небезопасно. Остановка потока с помощью Thread.stop заставляет его разблокировать все мониторы, которые он заблокировал (как естественное следствие неконтролируемого исключения ThreadDeath, распространяющегося по стеку). Если какой-либо из объектов, ранее защищенных этими мониторами, находился в противоречивом состоянии, поврежденные объекты становятся видимыми для других потоков, что может привести к произвольному поведению.

Подробнее по этой теме можно найти here.

Совершенно уверен, что вы можете выполнить свой запрос (хотя это не очень эффективный способ сделать это) - начать новый процесс Java через Runtime.exec(), а затем прекратить этот процесс по мере необходимости через Process.destroy(). Однако разделение между этими процессами не является тривиальным.

0

Вместо того, чтобы играть с запуском и остановкой потока, считаете ли вы, что поток отслеживает свойства, которые вы меняете через свой интерфейс? Вы в какой-то момент по-прежнему хотите остановить условие для своей нити, но это может быть сделано и так. Если вы являетесь поклонником MVC, это прекрасно вписывается в такой дизайн

Извините, после повторного чтения вашего вопроса ни эта, ни какая-либо другая опция «проверить переменную» не решит вашу проблему.

3

Разве это не похоже на вопрос «Как я могу прервать поток, если не доступен метод, отличный от Thread.stop()?»

Очевидно, что единственный действительный ответ - Thread.stop(). Его уродливые, могут портить вещи в некоторых обстоятельствах, могут привести к утечке памяти/ресурсов, и он недоволен TLEJD (Лигой экстраординарных разработчиков Java), однако в некоторых случаях это может быть полезно. В действительности нет никакого другого метода, если у стороннего кода нет доступного ему метода закрытия.

OTOH, иногда есть методы бэкдора близки. То есть, закрытие базового потока, с которым он работает, или какой-то другой ресурс, который ему нужно выполнить. Это редко бывает лучше, чем просто вызвать Thread.stop() и позволить ему испытать исключение ThreadDeathException.

1

Кажется, что у вас нет контроля над потоком, который отображает экран, но вы, похоже, имеете контроль над компонентом прядильщика. Я бы отключил счетчик, пока поток визуализирует экран. Таким образом, пользователь, по крайней мере, имеет некоторую обратную связь, связанную с их действиями.

1

Я полагаю, что вы просто предотвратить несколько потоков с помощью ждать и уведомить так, что если пользователь изменяет значение во много раз он будет работать только один раз тему. Если пользователи изменят значение в 10 раз, он сгорит Thread при первом изменении, а затем любые изменения, сделанные до того, как Thread завершится, все будут «свернуты» в одно уведомление. Это не остановит Thread, но нет хороших способов сделать это на основе вашего описания.

1

Решения, целью которых является использование булевого поля, являются правильное направление. Но поле должно быть неустойчивым. Язык Java Spec says:

«Например, в следующем (пунктирная) фрагмент кода, предположим, что this.done является не- летучий булево поле:

while (!this.done) 
    Thread.sleep(1000); 

Компилятор свободно читать поле this.done только один раз и повторно использовать кешированное значение в каждом выполнении цикла. Это будет означать, что цикл никогда не завершится, даже если другой поток изменил значение this.done. "

Насколько я помню "Java Concurrency in Pratice" цели использовать interrupt() и interrupted() методы java.lang.Thread.

1

Принятый ответ на этот вопрос позволяет отправить пакетную работу в фоновый поток. Это может быть лучше картина для этого:

public abstract class dispatcher<T> extends Thread { 

    protected abstract void processItem(T work); 

    private List<T> workItems = new ArrayList<T>(); 
    private boolean stopping = false; 
    public void submit(T work) { 
    synchronized(workItems) { 
     workItems.add(work); 
     workItems.notify(); 
    } 
    } 
    public void exit() { 
    stopping = true; 
    synchronized(workItems) { 
     workItems.notifyAll(); 
    } 
    this.join(); 
    } 
    public void run() { 
    while(!stopping) { 
     T work; 
     synchronized(workItems) { 
     if (workItems.empty()) { 
      workItems.wait(); 
      continue; 
     } 
     work = workItems.remove(0); 
     } 
     this.processItem(work); 
    } 
    } 
} 

Чтобы использовать этот класс, расширить его, обеспечивая тип для Т и реализацию processItem(). Затем просто постройте один и вызовите start() на нем.

Вы могли бы рассмотреть вопрос о добавлении метода abortPending:

public void abortPending() { 
    synchronized(workItems) { 
    workItems.clear(); 
    } 
} 

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

0

Правильный ответ - не использовать нить.

Вы должны использовать Executors см пакет: java.util.concurrent