2010-07-26 2 views
32

다음 코드는이 문제를 해결하려고합니다.스레드와 executor의 정상적인 종료

코드가 영원히 반복되고 처리 할 보류중인 요청이 있는지 확인합니다. 있는 경우, 새 스레드를 작성하여 요청을 처리하고이를 실행 프로그램에 제출합니다. 모든 스레드가 완료되면 60 초 동안 대기하고 보류중인 요청을 다시 확인합니다.

public static void main(String a[]){ 
    //variables init code omitted 
    ExecutorService service = Executors.newFixedThreadPool(15); 
    ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service); 
    while(true){ 
     List<AppRequest> pending = service.findPendingRequests(); 
     int noPending = pending.size(); 
     if (noPending > 0) { 
      for (AppRequest req : pending) { 
       Callable<Long> worker = new RequestThread(something, req); 
       comp.submit(worker); 
      } 
     } 
     for (int i = 0; i < noPending; i++) { 
      try { 
       Future<Long> f = comp.take(); 
       long name; 
       try { 
        name = f.get(); 
        LOGGER.debug(name + " got completed"); 
       } catch (ExecutionException e) { 
        LOGGER.error(e.toString()); 
       } 
      } catch (InterruptedException e) { 
       LOGGER.error(e.toString()); 
      } 
     } 
     TimeUnit.SECONDS.sleep(60); 
    } 

    } 

내 질문에 대한 대부분의 처리는 데이터베이스와 관련이 있습니다. 그리고이 프로그램은 Windows 컴퓨터에서 실행됩니다. 누군가가 시스템을 종료하거나 로그 오프하려고 할 때 이러한 스레드는 어떻게됩니까? 실행중인 스레드와 실행 프로그램을 정상적으로 종료하는 방법.

답변

55

일반적인 순서대로 종료가 같은 것을 볼 수 있습니다

final ExecutorService executor; 

Runtime.getRuntime().addShutdownHook(new Thread() { 
    public void run() { 
     executor.shutdown(); 
     if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional * 
      Logger.log("Executor did not terminate in the specified time."); //optional * 
      List<Runnable> droppedTasks = executor.shutdownNow(); //optional ** 
      Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional ** 
     } 
    } 
}); 

것은 당신이 로그인 할 수 있습니다 *을 유언 집행자는 당신이 기다릴 시간을 기다린 후에도 여전히 처리 할 과제를 가지고 있었다.
** Executor의 작업자 Threads가 강제로 현재 작업을 포기하고 나머지 작업을 시작하지 않도록 할 수 있습니다.

사용자가 java 프로세스에 인터럽트를 발행하거나 ExecutorService에 데몬 스레드 만있는 경우 위의 해결 방법이 작동합니다. 대신 ExecutorService에 완료되지 않은 비 데몬 스레드가있는 경우 JVM이 종료를 시도하지 않으므로 종료 후크가 호출되지 않습니다.

서비스가 아닌 개별 응용 프로그램 수명주기의 일부로 프로세스를 종료하려고 시도하면 종료 코드가 종료 훅 안에 있지 않아야하며 프로그램이 종료되도록 디자인 된 적절한 위치에 있어야합니다.

+0

이것은 거꾸로입니다. 'ExecutorService'가'Executors' 팩토리 메소드에 의해 리턴 된 전형적인 값 중 하나라고 가정하면, 백킹 쓰레드는 비 데몬 쓰레드입니다. 종료 훅은 그 쓰레드가 종료 될 때까지 호출되지 않으며'ExecutorService'가 종료 될 때까지 종료되지 않습니다. 실행 프로그램의 스레드가 비 데몬이거나 프로그램이 사용자 인터럽트를 수신하는 경우에만 솔루션이 사용됩니다. –

+0

OP는 JVM이 정상적으로 종료하도록 요청 받았는지 여부를 묻습니다. 그럼에도 불구하고 대답의 내용은 정확합니다. 실행 프로그램 서비스를 더 일찍 종료하기 위해 시스템 종료 훅에서이를 수행하지 않아도됩니다. –

+0

너무 빨리 답변 해 주셔서 감사합니다.나는 그들의 질문에 그 부분을 놓친 것 같다. 그 비트를 명확히하기 위해 세 번째''*** '를 추가 할 수 있습니까? –

7

이 책은 "Java Concurrency in Practice는"상태 :

7.4. JVM 종료

JVM은 순서대로 또는 갑작스러운 방식으로 종료 될 수 있습니다. 마지막 "정상"(nondaemon) 스레드 종료가, 누군가가 System.exit와, 를 호출하거나 (예 : SIGINT를 보내거나 Ctrl-C를 타격과 같은) 다른 플랫폼 고유의 수단 에 의해 때 질서 종료가 시작됩니다. [...]

7.4.1. 종료 훅

정상 종료시 JVM은 먼저 등록 된 모든 셧다운 훅을 시작합니다. 종료 훅은 으로 등록 된 시작되지 않은 스레드 입니다. Runtime.addShutdownHook. JVM을 사용하면 은 종료 훅이 시작되는 순서에 대해 보장하지 않습니다. 응용 프로그램 스레드 (데몬 또는 nondaemon)가 종료 시간이 일 때 계속 실행 중이면 을 종료하고 프로세스를 계속 실행합니다. 모든 종료 후크의 완료 후 이 완료되면 runFinalizersOnExit이 인 경우 JVM에서 finalizers를 실행하도록 선택한 다음 중지됩니다. JVM은 이 종료 시간에 실행중인 인 응용 프로그램 스레드 을 중지 또는 중단하지 못하게합니다. JVM 이 결국 중단되면 이 갑자기 종료됩니다. 종료 후크 또는 종료자가 완료되지 않은 경우 다음 순서대로 종료 프로세스 이 "중단"되고 JVM을 갑자기 종료해야합니다 ( ). [...]

중요한 비트는, "이 JVM 중지 또는 정지 종료 시간에 실행중인 모든 응용 프로그램 스레드를 방해하려는 시도를하지 않는다;. JVM이 결국 중단 때 갑자기 종료됩니다" 그래서 DB에 대한 연결이 갑자기 종료 될 것이라고 가정합니다. 시스템 종료를위한 후크가 없으면 프레임 워크를 사용하는 경우 정상적으로 종료 후크를 제공합니다. 내 경험에 의하면 DB에 대한 세션은 응용 프로그램이 DB 등으로 시간 초과 될 때까지 남아있을 수 있습니다. 그러한 후크없이 종료됩니다.

2

당신은 ExecutorServiceshutdown()를 호출 할 수 있습니다 :

는 이전에 제출 한 작업을 실행 을하는 순서대로 종료를 시작되지만 새 작업은 을 허용되지 않습니다.

또는 shutdownNow()를 호출 할 수

시도가 모두 적극적으로 실행 작업을 중지은 대기 중의 태스크의 처리 을 정지 해, 목록 실행을 대기하고 있던 태스크의 를 반환합니다.

적극적으로 작업을 처리하는 것을 막으려는 시도가 없습니다. 예를 들어, 의 일반적인 구현은 Thread.interrupt()를 통해 취소하므로 인터럽트에 응답하지 못한 모든 작업이 절대로 종료되지 않을 수 있습니다. 당신이 그것을 중지하는 방법을 심하게 의존 전화 한

.... ExecutorService를의

+0

내 웹 app.How에서 다음과 같은 문제가 발생하여 메모리 누수가 발생하지 않습니다 .http : //stackoverflow.com/questions/39166512/memory-leak-error-in-tomcat-server-even-after-removed-quartz -related-code – Vicky

5

shutdown()을 명시 적으로 호출하기위한 종료 후크를 추가 한 것이 나에게 적합하지 않았기 때문에 Google 구아바에 쉬운 해결책을 발견했습니다. com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService.

+1

'MoreExecutors.getExitingExecutorService'는'ThreadPoolExecutor' 인스턴스만을 받아들이 기 때문에 조금 문제가있다. 결과적으로 ExecutorService를 반환하는 Executors.newFixedThreadPool과 같이 단순히 executor를 수동으로 정의해야한다. – voo