기타/리펙토링

프로그래밍의 1초 정지는 어떻게 다뤄야 할까

oiNeh 2022. 7. 23. 08:55

코드 카타에서 알람시계를 만들면서

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();
    }

}

 

위 코드로 하면 전체적인 코드는 길어지겠지만 지저분해 보이는 코드를 없앴으니 일단 이득이다