Dev Language/EffectiveJava

[EffectiveJava] 프로그램의 동작을 스레드 스케줄러에 기대지 말라

ydin 2025. 5. 25. 19:42

요약

프로그램의 성능을 운영체제의 스레드 스케줄러에 의존하면 특정 운영체제의 의존도가 높아지기에 견고성과 이식성이 떨어지는 프로그램을 만들 가능성이 높다.

따라서 되도록이면 스레드 스케줄러에 독립적인 프로그램을 개발하도록 하자!

 

OS에 덜 의존적인 프로그램 작성하는 법

 

1. 견고하고 빠릿하고 이식성 좋은 프로그램

  1. 실행 가능한 스레드의 평균 개수프로세서 수보다 지나치게 많아지지 않도록 하자
    1. 스레드 스케줄러가 고민할 거리가 줄어들기 때문
  2. 실행 준비가 된 스레드들은 맡은 작업을 완료할 때까지 계속 실행되도록 만들자.
    1. 이런 구조라면 스레드 스케줄링 정책이 아주 상이한 시스템에서도 동작이 크게 달라지지 않기 때문
    2. 전체 스레드, 실행 가능한 스레드, 대기 중인 스레드를 구분해야 한다.

 

2. 실행 가능한 스레드 수 적게 유지하는 기법

  1. 스레드가 작업 완료 후 다음 작업까지는 대기해야한다
    1. 스레드는 당장 처리해야 할 작업이 없다면 실행돼서는 안된다
  2. 스레드는 절대 바쁜 대기(busy waiting) 상태가 되면 안된다
    1. 공유 객체의 상태가 바뀔 때까지 쉬지 않고 검사해서는 안된다는 의미
    2. 스레드가 필요도 없이 실행 가능한 상태인 시스템의 예시
    3. 바쁜 대기의 취약점
      1. 스레드 스케줄러의 변덕에 취약
      2. 프로세서에 큰 부담을 제공해 유용한 작업이 실행될 기회를 박탈
    4. 바쁜 대기 예시
      1. 스레드 1000개를 만들어 자바의 CountDownLatch와 비교할 때 약 10배의 성능 차이가 발생했다.
public class SlowCountDownLatch {
    private int count;

    public SlowCountDownLatch(int count) {
        if (count < 0) {
            throw new IllegalArgumentException(count + " < 0");
        }
        this.count = count;
    }

    public void await() {
        while (true) {
            synchronized (this) {
                if (count == 0) {
                    return;
                }
            }
        }
    }

    public synchronized void countDown() {
        if (count != 0) {
            count--;
        }
    }
}

 

3. Thread.yield 사용을 지양하자

특정 스레드가 다른 스레드들과 비교해 CPU 시간을 충분히 얻지 못해서 간신히 돌아가는 프로그램을 보더라도 Thread.yield는 사용하지 말자.

  • Thread.yield 메서드
    • 양보 : 현재 실행 대기 중인 동등한 우선순위 이상의 다른 스레드에게 실행기회를 제공한다
    • 즉, 실행 중인 스레드를 **실행 대기 상태(Runnable)로 변경**하는 메서드
    • 스레드 실행되었는데 스레드의 실행이 잠시동안 무의미한 경우가 발생하는데, 이때 CPU 자원의 낭비가 발생할 수 있다
  • 이유
    • 문제를 개선할 수는 있지만, 이식성 면에서 좋은 선택이 아니다
    • 테스트를 할 수단도 없다
OS1 
우선순위 B(1) > A(2)

thread A 우선순위 2-> 실행
thread B 우선순위 1(더 높은 우선순위)-> 나 급해. 
문제 해결 : thread A -> (yield) -> thread B 실행 but 급한 불만 끈 상태, 본질적인 문제 해결 x
thread A 실행된 이유 o. 실행이 완료가 되어야 하는데, 갑자기 대기 상태.

OS2
우선순위 A > B -> 의도대로 동작하지 않음!
의도 : B > A
  •  대안
    • 애플리케이션 구조를 바꿔 동시에 실행 가능한 스레드 수가 적어지도록 조치하자

 

4. 스레드 우선순위 사용을 지양하자

스레드 몇 개의 우선순위를 조율해서 애플리케이션의 반응 속도를 높이는 것도 타당할 수 있지만, 이것이 적합한 상황은 드물고 이식성이 떨어진다.

이미 잘 동작하는 프로그램의 서비스 품질 향상을 위해 드물게 사용될 수는 있지만, 간신히 동작하는 프로그램을 고치는 용도로 사용해서는 절대 안된다.