* port 연결 여부 확인 스케줄러
public int portWatcher(String port) throws InterruptedException {
final int[]count = {0};
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
CountDownLatch latch = new CountDownLatch(1);
Runnable checkPortTask = new Runnable() {
@Override
public void run(){
try {
Socket socketL = new Socket(host, port);
log.info("포트연결");
count[0]++;
latch.countDown();
executor.shutdown();
} catch (IOException e) {
log.info("포트연결X");
}
}
};
// 5초마다 포트 상태 확인
executor.scheduleAtFixedRate(checkPortTask, 0, 5, TimeUnit.SECONDS);
// 타임아웃 예외
if (!latch.await(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
throw new TimeoutException("포트연결확인 타임아웃");
}
return count[0];
}
ScheduledExecutorService의 scheduleAtFixedRate 메서드로 5초에 한번씩 포트연결 확인하는 스케줄러 생성.
CountDownLatch 의 초기 카운트 값을 1로 설정.
latch.await() 으로 현재 스레드 대기상태 유지. (최대 60초로 타임아웃 설정)
port 연결여부 확인 작업을 5초마다 반복.
port 연결이 확인되면 latch.countDown() 으로 카운트 값이 0이 되면서 작업 종료. (현재 스레드 대기상태 해제)
작업 종료를 알리는 count[0] 변수를 반환.
* 원래 아래처럼 스크립트 파일에서 실행했는데 오류가 나도 알수가 없어서 그냥 백단에서 연결확인로직 만들어봄.
while ! nc -z localhost $run_port; do
sleep 3 # 3초 대기 후 다시 확인
done
*** count 변수를 final 및 배열로 선언해야 하는 이유 ***
익명 클래스 or 람다식에서 사용되는 변수는 불변이어야 하기 때문.
( 익명 클래스 or 람다식 내부에서 외부 스코프에서 정의된 변수를 참조할 때 해당 변수를 캡쳐해서 사용.
=> 참조가 아닌 값을 복사하기 때문에 외부 변수와 복사본의 값이 달라질 수 있음. 따라서 불변이어야 함.)
또, ScheduledExecutorService를 사용하여 작업이 병렬로 실행될 수 있기 때문에 값의 안정성 보장 필요.
finial로 선언했기 때문에 변수의 참조값은 고정되지만, 배열이기 때문에 배열 요소의 값은 변경할 수 있음!
* ScheduledExecutorService
병렬 작업 스케줄링을 지원하는 인터페이스. ( ExecutorService* 의 하위 클래스 )
* ExecutorService : 쓰레드 풀 기반 작업관리를 위한 인터페이스.
1. schedule (지연 실행)
일정 시간 이후 작업 실행.
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
commnad : 실행할 작업(함수)
delay : 지연시간
unit : 시간 단위 ex) TimeUnit.SECONDS, TimeUnit.MILLISECONDS
ex)
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(() -> System.out.println("5초 후 실행"), 5, TimeUnit.SECONDS);
=> 실행하면 5초 후 출력.
2. scheduleAtFixedRate (반복 실행)
지정된 주기마다 작업 반복 실행.
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
commnad : 실행할 작업(함수)
initialDelay : 작업이 처음 실행되지 전까지 대기 시간.
period: 작업 실행 주기 (작업 시작 기준)
unit : 시간 단위 ex) TimeUnit.SECONDS, TimeUnit.MILLISECONDS
ex)
scheduler.scheduleAtFixedRate(() -> System.out.println("3초 간격 실행"), 0, 3, TimeUnit.SECONDS);
=> 실행하면 3초마다 출력.
** 작업시간이 주기보다 길면 오버로드 발생.
실행하는 작업이 주기(ex 3초)보다 길다면 작업이 완료되지 않아도 새 작업이 실행되기 때문!
3. scheduleWithFixedDelay (지연 반복 실행)
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
commnad : 실행할 작업(함수)
initialDelay : 첫 작업 실행 전 대기 시간.
delay: 작업 실행 주기 (작업 완료 기준)
unit : 시간 단위 ex) TimeUnit.SECONDS, TimeUnit.MILLISECONDS
ex)
scheduler.scheduleWithFixedDelay(() -> System.out.println("이전 작업 완료 후 3초 지연"), 0, 3, TimeUnit.SECONDS);
=> 실행하면 3초마다 출력.
** 작업시간이 주기보다 길어도 작업 완료 후 다음 작업을 실행하기 때문에 스케줄이 겹치지 않음!
(작업시간이 가변적인 경우 활용.)
* CountDownLatch
다수의 스레드 작업을 동기화하기 위한 클래스.
특정 작업이 완료될 때까지 다른 스레드가 대기하는 경우 사용.
(주요메소드)
- CountDownLatch(int count) : 초기 대기 카운트 설정하여 CountDownLatch 객체 생성.
- countDown() : 카운트를 1 감소. (카운트가 0이되면 모든 대기중인 스레드 해제.)
- await() : 카운트가 0 이 될 때까지 현재 스레드를 대기 상태로 유지.
- await(long timeout, TimeUnit unit) : timeout 시간동안 대기, unit 초과 시 false 반환 => 대기상태 해제
- getCount() : 남아있는 카운트 값 반환.
'Java > 공부공부' 카테고리의 다른 글
[JAVA] Thread.sleep() (0) | 2024.11.27 |
---|---|
[JAVA] JAR & WAR (0) | 2024.11.16 |
[기타] System.out.println 실무에서 사용하면 안되는 이유 (0) | 2024.11.16 |
[Java] 자바에서 리눅스 명령어 실행 (JSch , Session , ChannelExec ) (0) | 2024.11.06 |
[Java] Stream API (Collectors 클래스) (0) | 2024.10.29 |