Мне нужно решение, чтобы правильно остановить поток в Java.
У меня есть IndexProcessor
класс, который реализует интерфейс Runnable:
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
@Override
public void run() {
boolean run = true;
while (run) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
run = false;
}
}
}
}
И у меня есть ServletContextListener
класс, который запускает и останавливает поток:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
@Override
public void contextInitialized(ServletContextEvent event) {
thread = new Thread(new IndexProcessor());
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (thread != null) {
thread.interrupt();
LOGGER.debug("Thread successfully stopped.");
}
}
}
Но когда я закрываю tomcat, я получаю исключение в своем классе IndexProcessor:
2012-06-09 17:04:50,671 [Thread-3] ERROR IndexProcessor Exception
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at lt.ccl.searchengine.processor.IndexProcessor.run(IndexProcessor.java:22)
at java.lang.Thread.run(Unknown Source)
Я использую JDK 1.6. Итак, вопрос:
Как я могу остановить поток и не выбрасывать исключения?
PS Я не хочу использовать .stop();
метод, потому что он устарел.
java
multithreading
listener
Паулюс Матулионис
источник
источник
InterruptedException
. Это то, что я думаю, но мне также интересно, как стандартным образом.InterruptedException
можно найти по адресу ibm.com/developerworks/library/j-jtp05236 .Ответы:
В
IndexProcessor
классе вам нужен способ установки флага, который информирует поток о том, что он должен завершиться, подобно переменной,run
которую вы использовали только в области видимости класса.Когда вы хотите остановить поток, вы устанавливаете этот флаг и вызываете
join()
поток и ждете его завершения.Убедитесь, что флаг является потокобезопасным, используя переменную volatile или методы getter и setter, которые синхронизируются с переменной, используемой в качестве флага.
Тогда в
SearchEngineContextListener
:источник
Использование
Thread.interrupt()
- вполне приемлемый способ сделать это. На самом деле, это, вероятно, предпочтительнее флага, как предложено выше. Причина в том, что если вы находитесь в режиме прерывистого блокирующего вызова (например,Thread.sleep
или используете операции канала java.nio), вы действительно сможете выйти из этого сразу.Если вы используете флаг, вам нужно дождаться завершения операции блокировки, а затем вы можете проверить свой флаг. В некоторых случаях вы должны сделать это в любом случае, например, используя стандартные
InputStream
/OutputStream
которые не прерываются.В этом случае, когда поток прерывается, он не прерывает ввод-вывод, однако вы можете легко сделать это регулярно в своем коде (и вы должны делать это в стратегических точках, где вы можете безопасно остановить и очистить)
Как я уже сказал, главное преимущество
Thread.interrupt()
заключается в том, что вы можете немедленно прервать прерывистые вызовы, чего нельзя сделать с помощью флагового подхода.источник
interrupt()
может быть в порядке, но во многих других случаях это не так (например, если ресурс должен быть закрыт). Если кто-то изменит внутреннюю работу цикла, вам нужно помнить, чтобы перейтиinterrupt()
к логическому способу. Я бы пошел с безопасного пути с самого начала и использовал флаг.Простой ответ: Вы можете остановить поток ВНУТРЕННЕ одним из двух распространенных способов:
Вы также можете остановить темы ВНЕШНЕЕ:
system.exit
(это убивает весь ваш процесс)interrupt()
*kill()
илиstop()
)*: Ожидается, что это должно остановить поток. Однако то, что на самом деле делает поток, когда это происходит, полностью зависит от того, что написал разработчик, когда создавал реализацию потока.
Обычный шаблон, который вы видите в реализациях метода run, - это
while(boolean){}
где логическое значение обычно называется именемisRunning
, оно является переменной-членом своего класса потока, оно изменчиво и обычно доступно другим потокам с помощью метода установки типа, напримерkill() { isRunnable=false; }
. Эти подпрограммы хороши тем, что позволяют потоку высвобождать любые ресурсы, которые он держит, перед завершением.источник
Вы должны всегда заканчивать потоки, проверяя флаг в
run()
цикле (если есть).Ваша тема должна выглядеть так:
Затем вы можете закончить тему, позвонив
thread.stopExecuting()
. Таким образом, поток заканчивается чистым, но это занимает до 15 секунд (из-за вашего сна). Вы все еще можете вызвать thread.interrupt (), если это действительно срочно, но предпочтительным способом всегда должна быть проверка флага.Чтобы избежать ожидания в течение 15 секунд, вы можете разделить сон следующим образом:
источник
Thread
- он реализуетRunnable
- вы не можете вызыватьThread
методы для него, пока вы не объявите его как, иThread
в этом случае вы не можете вызватьstopExecuting()
Как правило, поток прерывается, когда он прерывается. Итак, почему бы не использовать родной логическое значение? Попробуйте isInterrupted ():
ref- Как я могу убить нить? без использования stop ();
источник
Для синхронизации потоков я предпочитаю использовать,
CountDownLatch
который помогает потокам ждать завершения процесса. В этом случае рабочий класс настроен сCountDownLatch
экземпляром с заданным количеством. Вызовawait
метода блокируется до тех пор, пока текущий счетчик не достигнет нуля из-за вызововcountDown
метода или заданного времени ожидания. Этот подход позволяет мгновенно прерывать поток, не дожидаясь истечения указанного времени ожидания:Если вы хотите , чтобы закончить выполнение другого потока, выполнить CountDown на
CountDownLatch
иjoin
нить к главной теме:источник
Некоторая дополнительная информация. И флаг, и прерывание предлагаются в документе Java.
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
Для потока, который ждет длительные периоды (например, для ввода), используйте
Thread.interrupt
источник
Я не получил прерывание для работы в Android, поэтому я использовал этот метод, работает отлично:
источник
volatile
. См. Docs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3 .Когда-нибудь я попробую 1000 раз в моем onDestroy () / contextDestroyed ()
источник