글을 작성하게 된 계기
분산 시스템을 학습하던 중, 단조 시계와 실제 시계를 알게 됐고, 이를 정리하기 위해 글을 작성하게 되었습니다.
1. 단조 시계
단조 시계(Monotonic Clock)는 시간이 항상 증가 하며, 외부 요인의 영향을 받지 않는 시계 입니다.
A monotonic clock is a time source that won’t ever jump forward or backward.
자바의 System.nanoTime( )이나 Clock.monotonic( ) 같은 함수가 이에 해당합니다. 이는 부팅 이후의 경과 시간을 측정하는 용도로 사용되며, 절대적인 현재 시각을 제공하지 않습니다. 예를 들어, 이는 실행 시간 이 얼마나 걸렸는지 확인하는 데 적합하며, 시스템 시간이 변경되더라도 영향을 받지 않으므로, 정확한 시간 간격을 측정할 수 있습니다.
1
2
3
4
long startTime = System.nanoTime(); // 시작 시간 기록
doSomething();
long elapsedTime = System.nanoTime() - startTime; // 경과 시간 계산
System.out.println("Elapsed time: " + elapsedTime + " ns");
하지만 System.nanoTime( )의 값 자체는 절대적인 의미가 없습니다. 즉, 현재 시간을 제공하지 않으며, 상대적인 시간 간격을 측정하는 데만 의미가 있습니다.
1
System.out.println("현재 시간: " + System.nanoTime()); // 의미 없는 값 출력
2. 실제 시계
실제 시계(Wall Clock, System Clock)는 운영체제에서 제공하는 현재 시간 을 의미합니다.
자바의 System.currentTimeMillis( )나 Instant.now( )가 이에 해당합니다. 이는 사람이 인식하는 현재 시각 을 제공하며, 로그 기록이나 타임스탬프 저장 같은 작업에 적합합니다.
1
2
System.out.println("현재 시간 (ms): " + System.currentTimeMillis());
System.out.println("현재 시간 (ISO 8601): " + Instant.now());
하지만 실제 시계는 시스템 시간이 변경될 수 있습니다. 예를 들어, 관리자나 NTP(Network Time Protocol) 동기화로 인해 시간이 갑자기 조정될 수도 있습니다. 이는 시간이 변경되거나, 시간 간격을 정확하게 측정해야 하는 경우, 실제 시계를 사용하면 문제가 발생할 수 있습니다. 아래 코드에서 5초를 기다렸지만, 시스템 시간이 10초 뒤로 조정되면 elapsedTime이 음수가 될 수도 있기 때문입니다.
1
2
3
4
long startTime = System.currentTimeMillis();
Thread.sleep(5_000);
long elapsedTime = System.currentTimeMillis() - startTime;
System.out.println("Elapsed time: " + elapsedTime + " ms");
3. 언제, 어떤 것을 사용해야 할까?
현재 시간을 확인할 때는 우리가 일반적으로 인식하는 실제 시각을 얻기 위해 실제 시계를 사용합니다. 실제 시계는 운영체제의 시스템 시간을 기반으로 동작하여, 현재 날짜와 시간을 제공하므로 로그 기록, 데이터베이스 저장, 사용자 인터페이스에 현재 시각을 표시하는 등의 용도에 적합합니다. 다만, 시스템 시간이 네트워크 동기화나 관리자의 수동 조정에 의해 변경될 수 있으므로, 시간 간격을 정확하게 측정하기에는 부적합합니다.
1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
System.out.println("현재 시간 (ms): " + System.currentTimeMillis());
System.out.println("현재 시간 (UTC): " + Instant.now());
System.out.println("현재 시간 (Asia/Seoul): " + LocalDateTime.now(ZoneId.of("Asia/Seoul")));
}
}
또한, 로그에 타임스탬프를 기록할 때도 사람이 이해할 수 있는 실제 시각 정보가 필요하므로, 실제 시계를 사용하는 것이죠.
1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
final String logTimestamp = LocalDateTime.now(ZoneId.of("Asia/Seoul")).toString();
System.out.println("[Log] " + logTimestamp + " - 서비스가 시작되었습니다.");
}
}
반면에, 시간 간격을 측정할 때는 코드 실행 시간, 성능 테스트, 또는 이벤트 간의 시간 차이를 정확하게 측정하기 위해 단조 시계를 사용합니다. 단조 시계는 시간이 항상 증가하며, 시스템 시간이 변경되더라도 그 값에 영향을 받지 않습니다. 단지 두 시점 간의 차이를 측정하는 데 매우 신뢰할 수 있는 결과를 제공합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
final long startTime = System.nanoTime(); // 벤치마킹 시작
runComputation(); // 실행할 연산
final long elapsedTime = System.nanoTime() - startTime; // 실행 시간 측정
System.out.println("연산 실행 시간 (ns): " + elapsedTime);
}
public static void runComputation() {
int sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += i;
}
}
}
4. 정리
단조 시계와 실제 시계는 각각 시간 측정의 목적에 따라 사용되며, 시간 간격을 측정할 때는 단조 시계를, 현재 시각을 확인할 때는 실제 시계를 사용하는 것이 바람직합니다. 단조 시계는 시간이 항상 증가하며, 시스템 시간 변경에 영향을 받지 않으므로 정확한 시간 간격을 측정하는 데 적합하며, 실제 시계는 운영체제의 시스템 시간을 기반으로 현재 날짜와 시간을 제공하므로 로그 기록, 데이터베이스 저장, 사용자 인터페이스에 현재 시각을 표시하는 등의 용도에 적합합니다.