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




Поток, который обновляет 3D-вид, должен периодически проверять какой-либо флаг (использовать volatile boolean), чтобы видеть, следует ли его завершать. Если вы хотите прервать поток, просто установите флаг. Когда поток в следующий раз проверяет флаг, он должен просто выйти из цикла, который он использует для обновления представления, и вернуться из своего метода run.
Если вы действительно не можете получить доступ к коду, в котором запущен поток, чтобы он проверил флаг, тогда нет безопасного способа остановить поток. Этот поток когда-либо завершается нормально до завершения вашего приложения? Если да, то что заставляет его останавливаться?
Если он работает в течение какого-то длительного периода времени, и вы просто должны его завершить, вы можете рассмотреть возможность использования устаревшего метода Thread.stop(). Однако он был объявлен устаревшим по уважительной причине. Если этот поток остановлен в середине какой-либо операции, которая оставляет что-то в несогласованном состоянии или какой-то ресурс не очищен должным образом, у вас могут быть проблемы. Вот примечание от документация:
This method is inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior. Many uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running. If the target thread waits for long periods (on a condition variable, for example), the interrupt method should be used to interrupt the wait. For more information, see Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?
Процесс обновления трехмерного вида в основном вызывает только один метод, выполнение которого очень долгое. Я не могу добавить какой-либо флаг в этот метод, так как у меня нет доступа к его коду. Так что здесь нельзя использовать это решение.
Решение, предложенное Дейвом Л., является правильным. Если у вас нет доступа к исходному коду стороннего кода, то, возможно, единственное, что у вас осталось, - это манипуляции с байт-кодом. Используя такую библиотеку, как ASM (asm.objectweb.org), вы можете изменить класс во время выполнения и «внедрить» свой собственный код.
Может это вам поможет: Как мы можем убить запущенный поток в Java?
You can kill a particular thread by setting an external class variable.
Class Outer
{
public static flag=true;
Outer()
{
new Test().start();
}
class Test extends Thread
{
public void run()
{
while(Outer.flag)
{
//do your work here
}
}
}
}
if you want to stop the above thread, set flag variable to
false. The other way to kill a thread is just registering it in ThreadGroup, then calldestroy(). This way can also be used to kill similar threads by creating them as group or register with group.
Если вы хотите, чтобы люди воспринимали вас серьезно, перестаньте использовать «u» и «ur». Потратьте время, чтобы ввести все эти дополнительные 2 символа - это не задержит вас так долго.
Я не тестировал его, но не думаю, что ThreadGroup.destroy() является решением: «Уничтожает эту группу потоков и все ее подгруппы. Эта группа потоков должна быть пустой, что означает, что все потоки, которые были в этой группе потоков, с тех пор остановлены. . "
Раньше я реализовал нечто подобное: в моем подклассе shutdown() был реализован метод Runnable, который устанавливает для переменной экземпляра should_shutdown значение true. Метод run() обычно делает что-то в цикле и периодически проверяет should_shutdown и, когда он истинен, возвращает или вызывает do_shutdown(), а затем возвращает.
Вы должны держать ссылку на текущий рабочий поток под рукой, и когда пользователь изменяет значение, вызовите shutdown() в текущем потоке и дождитесь его завершения. Затем вы можете запустить новую тему.
Я бы не рекомендовал использовать Thread.stop, поскольку он был устаревшим, когда я проверял его в последний раз.
Редактировать:
Прочтите свой комментарий о том, как ваш рабочий поток просто вызывает другой метод, выполнение которого требует времени, поэтому приведенное выше не применяется. В этом случае ваш единственный реальный вариант - попытаться вызвать в interrupt() и посмотреть, не повлияет ли это. Если нет, подумайте о том, чтобы вручную вызвать прерывание функции, которую вызывает ваш рабочий поток. Например, похоже, что он выполняет сложный рендеринг, поэтому, возможно, он уничтожит холст и вызовет исключение. Это не очень хорошее решение, но, насколько я могу судить, это единственный способ остановить поток в подобных ситуациях.
Поток завершится после завершения метода run(), поэтому вам нужна некоторая проверка, которая заставит его завершить метод.
Вы можете прервать поток, а затем выполнить некоторую проверку, которая будет периодически проверять isInterrupted() и возвращаться из метода run().
Вы также можете использовать логическое значение, которое периодически проверяется в потоке и заставляет его возвращаться, если это так, или помещать поток внутри цикла, если он выполняет какую-то повторяющуюся задачу, и затем он выйдет из метода run (), когда вы установите логическое значение. Например,
static boolean shouldExit = false;
Thread t = new Thread(new Runnable() {
public void run() {
while (!shouldExit) {
// do stuff
}
}
}).start();
К сожалению, убийство потока по своей сути небезопасно из-за возможности использования ресурсов, которые могут быть синхронизированы с помощью блокировок, и если поток, который вы убиваете в настоящее время, имеет блокировку, это может привести к тому, что программа войдет в тупик (постоянная попытка захватить ресурс, который не может быть получен) . Вам нужно будет вручную проверить, нужно ли его убивать из потока, который вы хотите остановить. Volatile обеспечит проверку истинного значения переменной, а не того, что могло быть сохранено ранее. Кстати, Thread.join в выходящем потоке, чтобы убедиться, что вы дождетесь, пока умирающий поток действительно не исчезнет, прежде чем что-либо делать, а не постоянно проверять.
Вместо того, чтобы устанавливать собственный логический флаг, почему бы просто не использовать механизм прерывания потока, уже присутствующий в потоках 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 / Thread:
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
}
}
Чтобы ответить на ваш вопрос, это связано с тем, что Thread.intrerrupt () может вызывать побочные эффекты, вызывая исключения во время определенных операций ввода-вывода или параллелизма. Иногда это именно то, что вам нужно, но не всегда.
Поскольку вы имеете дело с кодом, к которому у вас нет доступа, вероятно, вам не повезло. Стандартная процедура (как указано в других ответах) состоит в том, чтобы иметь флаг, который периодически проверяется запущенным потоком. Если флаг установлен, выполните очистку и выйдите.
Поскольку этот параметр вам недоступен, единственный другой вариант - принудительно завершить запущенный процесс. Раньше это было возможно с помощью вызова Thread.stop (), но этот метод навсегда устарел по следующей причине (скопирован из javadocs):
This method is inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior.
Более подробную информацию по этой теме можно найти здесь.
Один из абсолютно надежных способов выполнить свой запрос (хотя это не очень эффективный способ сделать это) - запустить новый Java-процесс через Runtime.exec (), а затем остановить этот процесс по мере необходимости через Process.destroy (). Однако разделение состояния между такими процессами не совсем тривиально.
Рассматривали ли вы, что вместо того, чтобы играть с запуском и остановкой потока, чтобы поток наблюдал за свойствами, которые вы изменяете через интерфейс? В какой-то момент вам все еще понадобится условие остановки для вашего потока, но это можно сделать, это тоже было. Если вы поклонник MVC, это прекрасно вписывается в такой дизайн.
К сожалению, после повторного прочтения вашего вопроса ни этот, ни какой-либо другой вариант проверки переменных не решит вашу проблему.
Разве это не похоже на вопрос: «Как я могу прервать поток, если не доступен другой метод, кроме Thread.stop ()?»
Очевидно, единственный правильный ответ - Thread.stop (). Это уродливо, может сломать вещи при некоторых обстоятельствах, может привести к утечкам памяти / ресурсов и не одобряется TLEJD (Лига выдающихся разработчиков Java), однако в некоторых случаях, подобных этому, он все же может быть полезен. На самом деле нет другого метода, если для стороннего кода нет доступного метода закрытия.
OTOH, иногда есть методы закрытия бэкдора. То есть закрытие базового потока, с которым он работает, или какого-либо другого ресурса, который ему нужен для выполнения своей работы. Однако это редко бывает лучше, чем просто вызвать Thread.stop () и дать ему исключение ThreadDeathException.
Кажется, что у вас нет никакого контроля над потоком, который отображает экран, но вы, похоже, контролируете компонент прядильщика. Я бы отключил счетчик, пока поток отображает экран. Таким образом, у пользователя будет хотя бы некоторая обратная связь, касающаяся его действий.
Попробуйте interrupt (), как говорят некоторые, чтобы посмотреть, не повлияет ли это на ваш поток. Если нет, попробуйте уничтожить или закрыть ресурс, который остановит поток. Это может быть немного лучше, чем попытка бросить на него Thread.stop ().
Если производительность приемлемая, вы можете рассматривать каждое 3D-обновление как дискретное непрерывное событие и просто позволить ему пройти до завершения, после чего проверять, есть ли новое последнее обновление, которое нужно выполнить. Это может сделать графический интерфейс немного нестабильным для пользователей, поскольку они смогут внести пять изменений, затем увидеть графические результаты того, как все было пять изменений назад, а затем увидеть результат своего последнего изменения. Но в зависимости от того, как долго длится этот процесс, он может быть терпимым и позволит избежать необходимости убивать поток. Дизайн может выглядеть так:
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;
}
Это будет моим решением, спасибо. Использование одного потока с циклом вместо создания каждый раз нового кажется хорошим вариантом. Я сделал лишь небольшое изменение: я вызываю perform3dUpdate () только в том случае, если latestArgs были изменены с момента последнего прохода.
Рад, что смог помочь, джумар! Это был мой первый пост о stackoverflow; Я подписался специально, чтобы опубликовать эту идею. :)
Две модификации, которые я внесу в endThread: 1. Если у вас есть доступ к объекту потока для текущего класса, я бы прервал его, чтобы прервать вызов sleep () раньше. 2. Опять же, если вы можете найти поток, выполните thread.join () в конце endThread ().
Это неверно в соответствии со спецификацией Java. флаг должен быть изменчивым, или доступ к нему должен осуществляться с помощью synchronize.
Вы должны использовать переменную условия для управления ожиданием обновляемых параметров. Сон в течение 500 миллисекунд может длиться дольше, но никогда не короче. Возможно, вместо этого вы хотели спать не более 500 миллисекунд или до тех пор, пока настройки не будут обновлены снова. Вы можете использовать внутренние блокировки с Object # wait (long) и Object # notify (), но я предпочитаю использовать явные типы Lock и Condition в пакете java.util.concurrent.locks. Переписать пример кода - это больше, чем я могу вместить в такой комментарий. Если вам нужна дополнительная помощь в реализации этих идей, сообщите об этом.
Я предлагаю вам просто предотвратить несколько потоков, используя wait и notify, чтобы, если пользователь меняет значение много раз, он запускал поток только один раз. Если пользователи изменят значение 10 раз, поток отключится при первом изменении, а затем любые изменения, сделанные до того, как поток будет завершен, все «сворачиваются» в одно уведомление. Это не остановит поток, но нет хороших способов сделать это на основе вашего описания.
Решения, которые используют логическое поле, являются правильным направлением. Но поле должно быть непостоянным. Спецификация языка Java говорит:
"For example, in the following (broken) code fragment, assume that this.done is a non- volatile boolean field:
while (!this.done) Thread.sleep(1000);The compiler is free to read the field this.done just once, and reuse the cached value in each execution of the loop. This would mean that the loop would never terminate, even if another thread changed the value of this.done."
Насколько я помню, «Параллелизм Java на практике» предназначен для использования методов прерывать() и прерванный () java.lang.Thread.
Принятый ответ на этот вопрос позволяет отправлять пакетную работу в фоновый поток. Это может быть лучшим шаблоном для этого:
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);
}
}
}
Чтобы использовать этот класс, расширьте его, предоставив тип для T и реализацию processItem (). Затем просто создайте один и вызовите для него start ().
Вы можете подумать о добавлении метода abortPending:
public void abortPending() {
synchronized(workItems) {
workItems.clear();
}
}
для тех случаев, когда пользователь опередил механизм рендеринга, и вы хотите отказаться от работы, которая была запланирована на данный момент.
Большой! Но если я правильно понял - мы не можем закончить поток как есть, мы можем только очистить список потоков? Но в таком случае эта ветка все равно будет работать?
Нет. Для этого и нужен exit ().
Ах, это твой собственный метод, не отмененный ... Понятно, спасибо. Но зачем называть notifyAll () список workItems? Разве недостаточно установить для переменной stopping значение true, потому что метод run () еще работает?
Если поток в run () ожидает в очереди, вы должны разбудить его, чтобы тест прошел. В противном случае он может заблокироваться на неопределенный срок, и join () никогда не завершится.
Правильный ответ - не использовать нить.
Вы должны использовать Executors, см. Пакет: java.util.concurrent
Прошу прощения, но говорю, что исполнители лучше потоков, без объяснения причин, это бесполезно. Поэтому я спрашиваю вас: в чем исполнители лучше потоков? Они лучше всегда или бывают случаи, когда потоки предпочтительнее?
Не могли бы вы дать нам дополнительную информацию об используемом вами API? Вы говорите, что отправляете что-то в какой-то метод из сторонней библиотеки, а это занимает много времени. Изменяет ли этот метод непосредственно переданный вами объект? Или вы получите обратный звонок с результатами?