Java에서 스레드를 올바르게 중지하는 솔루션이 필요합니다.
나는이 IndexProcessor
실행 가능한 인터페이스를 구현하는 클래스를 :
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.");
}
}
}
그러나 바람둥이를 종료하면 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을 사용하고 있습니다. 따라서 질문은 다음과 같습니다.
스레드를 중지하고 예외를 발생시키지 않는 방법은 무엇입니까?
추신 : 나는 .stop();
더 이상 사용되지 않기 때문에 방법 을 사용하고 싶지 않습니다.
답변
IndexProcessor
클래스 에서는 run
클래스 범위에서 방금 사용한 변수와 유사하게 스레드에 종료해야 함을 알리는 플래그를 설정하는 방법이 필요합니다 .
스레드를 중지하려면이 플래그를 설정 join()
하고 스레드를 호출 하고 완료 될 때까지 기다리십시오.
휘발성 변수를 사용하거나 플래그로 사용되는 변수와 동기화되는 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.");
}
}
}
답변
사용하는 Thread.interrupt()
것이 완벽하게 허용되는 방법입니다. 사실, 위에서 제안한 것처럼 플래그를 선호 할 것입니다. 그 이유는 Thread.sleep
java.nio Channel 작업을 사용하거나 사용하는 것과 같이 인터럽트 가능한 차단 호출을 수행하는 경우 실제로 즉시 차단할 수 있기 때문입니다.
플래그를 사용하는 경우 차단 작업이 완료 될 때까지 기다려야합니다. 그러면 플래그를 확인할 수 있습니다. 어떤 경우 에는 중단 할 수없는 표준 InputStream
/ OutputStream
을 사용하는 것과 같이 어쨌든이 작업을 수행해야합니다 .
이 경우 스레드가 중단되면 IO가 중단되지 않지만 코드에서 쉽게 일상적으로 수행 할 수 있습니다 (안전하게 중지하고 정리 할 수있는 전략적 지점 에서이 작업을 수행해야 함)
if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}
내가 말했듯이, 주요 장점 Thread.interrupt()
은 인터럽트 가능한 호출을 즉시 중단 할 수 있다는 것입니다. 플래그 접근으로는 할 수 없습니다.
답변
간단한 대답 : 두 가지 일반적인 방법 중 하나로 스레드를 내부적으로 중지 할 수 있습니다.
- run 메소드는 리턴 서브 루틴에 도달합니다.
- Run 메소드가 완료되고 내재적으로 리턴됩니다.
스레드를 외부 적으로 중지 할 수도 있습니다.
- 전화
system.exit
(전체 프로세스를 종료) - 스레드 객체의
interrupt()
메소드 호출 * - 스레드가 같은 소리 일 것이라고 구현 된 방법을 (같은이 있는지
kill()
또는stop()
)
* : 스레드를 중지해야합니다. 그러나 스레드가 실제로 발생할 때 스레드가 실제로하는 것은 개발자가 스레드 구현을 작성할 때 작성한 내용에 달려 있습니다.
run 메소드 구현에서 볼 수있는 일반적인 패턴은이며 while(boolean){}
, 부울은 일반적으로 이름이 지정된 것으로 isRunning
, 스레드 클래스의 멤버 변수이고, 휘발성이며, 일반적으로 setter 메소드 (예 :)를 통해 다른 스레드가 액세스 할 수 있습니다 kill() { isRunnable=false; }
. 이 서브 루틴은 스레드가 종료하기 전에 보유한 모든 자원을 해제 할 수있게 해주므로 좋습니다.
답변
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) {
...
답변
일반적으로 스레드는 중단되면 종료됩니다. 그렇다면 기본 부울을 사용하지 않는 이유는 무엇입니까? 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();
답변
스레드 동기화의 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;
}
}
}
다른 스레드의 실행을 마치려면 CountDownLatch
및 join
에서 스레드를 메인 스레드로 countDown을 실행 하십시오.
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.");
}
}
}
답변
일부 보충 정보. 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();
}