Study/Java

Java에서 일정 시간 후에 실행을 중지하는 방법

오늘만 사는 여자 2023. 12. 4. 10:17
728x90
반응형

1. 개요

이 기사에서는 특정 시간 후에 장기 실행을 종료하는 방법에 대해 알아 봅니다. 이 문제에 대한 다양한 솔루션을 탐색 할 것입니다. 또한 우리는 그들의 함정 중 일부를 다룰 것입니다.

 

2. 루프 사용

전자 상거래 응용 프로그램에서 제품 항목의 일부 세부 정보와 같이 여러 항목을 루프로 처리하고 있지만 모든 항목을 완료 할 필요는 없을 수 있다고 가정 해보십시오.

사실, 우리는 특정 시간까지만 처리하고 싶고 그 후에는 실행을 중지하고 그 시간까지 List이 처리 한 내용을 표시하려고합니다.

간단한 예를 보겠습니다.

long start = System.currentTimeMillis();
long end = start + 30*1000;
while (System.currentTimeMillis() < end) {
    // Some expensive operation on the item. 
}

여기에서 시간이 30 초 제한을 초과하면 루프가 중단됩니다. 위의 솔루션에는 몇 가지 주목할만한 점이 있습니다.

  • 낮은 정확도 : 루프가 부과 된 시간 제한보다 오래 실행될 수 있습니다 . 이는 각 반복에 걸리는 시간에 따라 다릅니다. 예를 들어 각 반복에 최대 7 초가 소요될 수있는 경우 총 시간은 최대 35 초가 될 수 있으며, 이는 원하는 시간 제한 인 30 초보다 약 17 % 더 길어집니다.
  • 차단 : 메인 스레드에서의 이러한 처리는 오랫동안 차단 될 수 있으므로 좋은 생각이 아닐 수 있습니다 . 대신, 이러한 작업은 주 스레드에서 분리되어야합니다.

다음 섹션에서는 인터럽트 기반 접근 방식이 이러한 제한을 제거하는 방법에 대해 설명합니다.

 

3. 인터럽트 메커니즘 사용

여기서는 장기 실행 작업을 수행하기 위해 별도의 스레드를 사용합니다. 주 스레드는 시간 초과시 작업자 스레드에 인터럽트 신호를 보냅니다.

작업자 스레드가 아직 살아 있으면 신호를 포착하고 실행을 중지합니다. 작업자가 시간 초과 전에 완료되면 작업자 스레드에 영향을주지 않습니다.

작업자 스레드를 살펴 보겠습니다.

class LongRunningTask implements Runnable {
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            // log error
        }
    }
}

여기서 Thread.sleep 은 장기 실행 작업을 시뮬레이션합니다. 대신 다른 작업이있을 수 있습니다. 모든 작업이 인터럽트 가능한 것은 아니므로 인터럽트 플래그  확인하는 것이 중요합니다 . 따라서 이러한 경우 수동으로 플래그를 확인해야합니다.

또한 모든 반복에서이 플래그를 확인하여 스레드가 최대 한 번의 반복 지연 내에 자체 실행을 중지하는지 확인해야합니다.

다음으로 인터럽트 신호를 보내는 세 가지 메커니즘에 대해 설명합니다.

3.1. 타이머 사용

또는 TimerTask  만들어 시간 초과시 작업자 스레드를 중단 할 수 있습니다 .

class TimeOutTask extends TimerTask {
    private Thread t;
    private Timer timer;

    TimeOutTask(Thread t, Timer timer){
        this.t = t;
        this.timer = timer;
    }
 
    public void run() {
        if (t != null && t.isAlive()) {
            t.interrupt();
            timer.cancel();
        }
    }
}

여기서 우리는 생성시 작업자 스레드를 받는 TimerTask  정의했습니다 . 그것은 것 그것의 호출에 작업자 스레드를 중단 실행 방법 . 타이머  트리거됩니다 TimerTask를  지정된 지연 후를 :

Thread t = new Thread(new LongRunningTask());
Timer timer = new Timer();
timer.schedule(new TimeOutTask(t, timer), 30*1000);
t.start();

 

3.2. Future # get 메소드 사용

Timer 를 사용하는 대신 Future  get 메소드를 사용할 수도 있습니다 .

ExecutorService executor = Executors.newSingleThreadExecutor();
Future future = executor.submit(new LongRunningTask());
try {
    f.get(30, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    f.cancel(true);
} finally {
    service.shutdownNow();
}

여기서는 ExecutorService  사용하여 Future 인스턴스를 반환하는 작업자 스레드를 제출했으며  get 메서드는 지정된 시간까지 주 스레드를 차단합니다. 그것은 올릴 것이다 TimeoutException을 지정된 제한 시간 후. 에서 캐치 블록, 우리는이 호출하여 작업자 스레드를 중단하는 취소 온 방법을 F의 uture의 객체입니다.

이전 접근 방식에 비해이 접근 방식의 주요 이점은 풀을 사용하여 스레드를 관리하는 반면 Timer 는 단일 스레드 (풀 없음) 만 사용한다는 것 입니다.

 

3.3. ScheduledExcecutorSercvice 사용

ScheduledExecutorService  사용 하여 작업을 중단 할 수도 있습니다 . 이 클래스는 ExecutorService 의 확장 이며 실행 예약을 처리하는 여러 메서드를 추가하여 동일한 기능을 제공합니다. 이것은 설정된 시간 단위의 특정 지연 후에 주어진 작업을 실행할 수 있습니다.

ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
Future future = executor.submit(new LongRunningTask());
executor.schedule(new Runnable(){
    public void run(){
        future.cancel(true);
    }
}, 1000, TimeUnit.MILLISECONDS);
executor.shutdown();

여기서 우리는 newScheduledThreadPool 메소드를 사용하여 크기가 2 인 예약 된 스레드 풀을 생성했습니다 . ScheduledExecutorService를 # 1 예약  방법은 소요 의 Runnable , 지연 값 및 지연 유닛.

위 프로그램은 제출 시점으로부터 1 초 후에 실행할 작업을 예약합니다. 이 작업은 원래의 장기 실행 작업을 취소합니다.

이전 접근 방식과 달리 Future # get 메서드를 호출하여 기본 스레드를 차단하지 않습니다 . 따라서 위에서 언급 한 모든 접근 방식 중에서 가장 선호되는 접근 방식 입니다.

 

 

출처 : Java에서 일정 시간 후에 실행을 중지하는 방법 (tistory.com)

728x90
반응형