Вопрос по java, listener, multithreading – Как правильно остановить поток в Java?
Мне нужно решение, чтобы правильно остановить поток в 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. Итак, вопрос:
Как я могу остановить поток и не выбрасывать какие-либо исключения?
P.S. Я не хочу использовать.stop();
метод, потому что это устарело.
InterruptedException
можно найти наibm.com/developerworks/library/j-jtp05236.
Daniel
InterruptedException
, Это то, что я думаю, но мне также интересно, как стандартным образом.
nhahtdh
join()
?
Havelock
И флаг, и прерывание предлагаются в документе Java.
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
private volatile Thread blinker;
public void stop() {
blinker = null;
}
public void run() {
Thread thisThread = Thread.currentThread();
while (blinker == thisThread) {
try {
Thread.sleep(interval);
} catch (InterruptedException e){
}
repaint();
}
}
Для потока, который ожидает длительные периоды (например, для ввода), используйтеThread.interrupt
public void stop() {
Thread moribund = waiter;
waiter = null;
moribund.interrupt();
}
когда он прерывается. Итак, почему бы не использовать родной логическое значение? Попробуйте isInterrupted ():
Thread t = new Thread(new Runnable(){
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){
// do stuff
}
}});
t.start();
// Sleep a second, and then interrupt
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
t.interrupt();
Вы можете остановить поток ВНУТРИ одним из двух распространенных способов:
The run method hits a return subroutine. Run method finishes, and returns implicitly.Вы также можете остановить темы ВНЕШНЕЕ:
Callsystem.exit
(this kills your entire process)
Call the thread object's interrupt()
method *
See if the thread has an implemented method that sounds like it would work (like kill()
or stop()
)
*: Ожидается, что это должно остановить поток. Однако то, что на самом деле делает поток, когда это происходит, полностью зависит от того, что написал разработчик, когда создавал реализацию потока.
Общий шаблон, который вы видите в реализациях метода run, этоwhile(boolean){}
где логическое значение обычно называетсяisRunning
он является переменной-членом своего класса потока, он является энергозависимым и обычно доступен другим потокам с помощью метода установки типа, например.kill() { isRunnable=false; }
, Эти подпрограммы хороши тем, что позволяют потоку высвобождать любые ресурсы, которые он держит, перед завершением.
Thread.interrupt()
это вполне приемлемый способ сделать это. Фактически, это, вероятно, предпочтительнее флага, как предложено выше. Причина в том, что если вы находитесь в режиме прерываемой блокировки (например,Thread.sleep
или используя операции канала java.nio), вы действительно сможете выйти из этого сразу.
Если вы используете флаг, вам нужно дождаться завершения операции блокировки, а затем вы можете проверить свой флаг. В некоторых случаях вы должны сделать это в любом случае, например, используя стандартныеInputStream
/OutputStream
которые не прерываются.
В этом случае, когда поток прерывается, он не прерывает ввод-вывод, однако вы можете легко сделать это регулярно в своем коде (и вы должны делать это в стратегических точках, где вы можете безопасно остановить и очистить)
if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}
Как я уже сказал, главное преимуществоThread.interrupt()
является то, что вы можете немедленно прервать прерывистые вызовы, что вы не можете сделать с подходом флага.
interrupt()
может быть в порядке, но во многих других случаях это не так (например, если ресурс должен быть закрыт). Если кто-то изменит внутреннюю работу цикла, вы должны помнить, чтобы изменитьinterrupt()
к логическому пути. Я бы пошел с безопасного пути с самого начала и использовал флаг.
мы можем вызвать устаревший метод Thread stop (), чтобы завершить его. Использование stop () невероятно опасно, так как это убьет ваш поток, даже если он находится в середине чего-то важного. Нет способа защитить себя, поэтому, если вы заметите код, который использует stop (), вам следует нахмуриться.
Как мы аккуратно закрываем поток?
В Java запуск потоков очень прост, но его закрытие требует много внимания и усилий.
Вот как это разработано в Java. В каждом потоке Java есть флаг, называемый флагом состояния прерывания, который мы можем установить снаружи, т.е. в родительском или основном потоке. И поток может время от времени проверять это и останавливает его выполнение. Добровольно..!! Вот как:
Thread loop = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (Thread.interrupted()) {
break;
}
// Continue to do nothing
}
}}); loop.start(); loop.interrupt();
CountDownLatch
что помогает потокам ждать завершения процесса. В этом случае рабочий класс настроен сCountDownLatch
экземпляр с заданным количеством. Вызовawait
метод будет блокироваться, пока текущий счетчик не достигнет нуля из-за вызововcountDown
метод или установлен тайм-аут. Этот подход позволяет мгновенно прерывать поток, не дожидаясь истечения указанного времени ожидания:
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private final CountDownLatch countdownlatch;
public IndexProcessor(CountDownLatch countdownlatch) {
this.countdownlatch = countdownlatch;
}
public void run() {
try {
while (!countdownlatch.await(15000, TimeUnit.MILLISECONDS)) {
LOGGER.debug("Processing...");
}
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
run = false;
}
}
}
Когда вы хотите закончить выполнение другого потока, выполните countDown наCountDownLatch
а такжеjoin
поток в основной поток:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
private IndexProcessor runnable = null;
private CountDownLatch countdownLatch = null;
@Override
public void contextInitialized(ServletContextEvent event) {
countdownLatch = new CountDownLatch(1);
Thread thread = new Thread(new IndexProcessor(countdownLatch));
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 (countdownLatch != null)
{
countdownLatch.countDown();
}
if (thread != null) {
try {
thread.join();
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
}
LOGGER.debug("Thread successfully stopped.");
}
}
}
IndexProcessor
класс, ам нужен способ устаноки флага, который сообщает потоку, что он должен будет заершиться, аналогично переменнойrun
что ы использоали только области идимости класса.
Когда ы хотите останоить поток, ы устаналиаете этот флаг и ызыаетеjoin()
на етке и дождаться его окончания.
Убедитесь, что флаг яляется потокобезопасным, используя переменную volatile или методы getter и setter, которые синхронизируются с переменной, используемой качесте флага.
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean running = true;
public void terminate() {
running = false;
}
@Override
public void run() {
while (running) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
running = false;
}
}
}
}
Затем SearchEngineContextListener
:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
private IndexProcessor runnable = null;
@Override
public void contextInitialized(ServletContextEvent event) {
runnable = new IndexProcessor();
thread = new Thread(runnable);
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) {
runnable.terminate();
thread.join();
LOGGER.debug("Thread successfully stopped.");
}
}
}
run()
цикл (если есть).
Ваша тема должна выглядеть так:
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean execute;
@Override
public void run() {
this.execute = true;
while (this.execute) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
this.execute = false;
}
}
}
public void stopExecuting() {
this.execute = false;
}
}
Затем вы можете закончить тему, позвонивthread.stopExecuting()
, Таким образом, поток заканчивается чистым, но это занимает до 15 секунд (из-за вашего сна).
Вы все еще можете вызвать thread.interrupt (), если это действительно срочно, но предпочтительным способом всегда должна быть проверка флага.
Чтобы избежать ожидания в течение 15 секунд, вы можете разделить сон следующим образом:
...
try {
LOGGER.debug("Sleeping...");
for (int i = 0; (i < 150) && this.execute; i++) {
Thread.sleep((long) 100);
}
LOGGER.debug("Processing");
} catch (InterruptedException e) {
...
поэтому я использовал этот метод, работает отлично:
boolean shouldCheckUpdates = true;
private void startupCheckForUpdatesEveryFewSeconds() {
threadCheckChat = new Thread(new CheckUpdates());
threadCheckChat.start();
}
private class CheckUpdates implements Runnable{
public void run() {
while (shouldCheckUpdates){
System.out.println("Do your thing here");
}
}
}
public void stop(){
shouldCheckUpdates = false;
}
volatile
, Увидетьdocs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3.