Как прервать поток быстрым и чистым способом в java?

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

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

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

Ответ 1

Попробуйте прерывать(), как говорили некоторые, чтобы определить, имеет ли значение ваш поток. Если нет, попробуйте уничтожить или закрыть ресурс, который остановит поток. У этого есть шанс быть немного лучше, чем пытаться бросить 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;
}

Ответ 2

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

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

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

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

Ответ 3

Вместо того, чтобы сворачивать свой собственный логический флаг, почему бы просто не использовать механизм прерывания потока уже в потоках 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
    }
}

Ответ 4

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

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

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

Ответ 5

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

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

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

Edit:

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

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

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

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

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

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

Ответ 11

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

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();
  }
}

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

Ответ 12

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

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

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

Более подробную информацию по этой теме можно найти здесь.

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

Ответ 13

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

Извините, после повторного чтения вашего вопроса ни эта, ни какая-либо другая опция "check variable" не решит вашу проблему.

Ответ 14

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

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

Ответ 15

Возможно, это может вам помочь: Как мы можем убить текущий поток в 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(). Этот способ также можно использовать для уничтожения похожих потоков, создавая их как группу или регистрируя группу.