프로그래밍의 1초 정지는 어떻게 다뤄야 할까
코드 카타에서 알람시계를 만들면서
1초마다 시간이 달라지는 것을 출력을 해야 하는 상황이 나왔는데
이럴 때 1초 정지는 어떻게 해줘야 좋을까
1초를 정직하게 정지한다
Thread.sleep(1000)
- 이렇게 1초를 정지해도 프로그램상 문제는 거의 없다
하지만 만약에 낮은 확률이긴 아마도 0.1% 확률로 알람을 무시할 수 있다
3초 4.9999초.. 6.0001초
실제 코드에서 1초 정지를 확인해본 결과
=============
|| test case 1 ||
=============
| 알람 시간 | | 초 | |밀리초 |
7:30:19초 1_658_529_020. 974
7:30:20초 1_658_529_021. 983
7:30:21초 1_658_529_022. 991
7:30:22초 1_658_529_024. 003
7:30:24초 1_658_529-025. 015
7:30:25초 1_658_529_026. 026
=============
|| test case 2 ||
=============
| 알람 시간 | | 초 | |밀리초 |
7:34:28초 1_658-529-269. 979
7:34:29초 1_658_529-270. 988
7:34:30초 1_658_529-272. 000
7:34:32초 1-658_529-273. 008
7:34:33초 1-658_529_274. 019
! ! ! { { { 테스트를 해보니까 1초가 증발했다. } } } ! ! !
원인은 아마도 스레드와 실행시간의 총합이 1초를 넘어서 그런 것 같다.
아래 그림으로 예시를 보여주자면
스레드 정지 (1초) + 실행시간(x초) = 한 턴 총 실행 시간( 1+x초)
보시다시피 1초 정지는 불안전하다
물론 1초마다 실행할 수 있게 만들 수 있을 것이다
(밀리초까지 설계해서 만든다면...)
하지만 여기서는 그 정도의 필요성은 없으니 다른 방법을 써보자
그래서 찾아낸 방법이
Timer 를 사용하는 것이다
Timer 에는 스케줄러를 지원해주는데 이 기능을 사용하면 거의 정확하게 정지후 실행을 할수 있다.
밀리초 차이는 있어도 크게 벗어나지 않는다.
new Timer().scheduleAtFixedRate(new TimerTask(){
@Override
public void run() {
//로직
}
},new Date(),1_000);
위 코드를 사용하면 안정적으로 1초마다 실행이 가능하다
하지만
괄호 안에 Override 하고 그 안에 로직을 넣는 건 살짝 지저분해 보인다
그래서 나는 다음 방식을 택했다
public void run() {
new Timer().scheduleAtFixedRate(new ScheduleTask(() -> {
time.secondUp();
alarm.checkAlarm(time);
}), new Date(), 1_000);
}
private static class ScheduleTask extends TimerTask {
private final Runnable runnable;
public ScheduleTask(Runnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
runnable.run();
}
}
위 코드로 하면 전체적인 코드는 길어지겠지만 지저분해 보이는 코드를 없앴으니 일단 이득이다