Item 84. Thread Scheduler

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

운영체제의 스레드 스케줄러는 작업을 공정하게 수행하려 하지만, 구체적인 정책은 운영체제마다 다를 수 있다. 운영체제 정책에 따라 정확성이나 성능이 달라지게 되면, 다른 플랫폼에 이식하기 어려워지기 때문에 이러한 정책에 의존해서는 안된다. 스레드 스케줄러에 의존하지 않게 하기 위해선 아래 세 가지 방법을 사용할 수 있다.

1. 실행 가능한 스레드의 수를 적게 유지

가장 좋은 방법은 실행 가능한 스레드의 수를 프로세서 수보다 지나치게 많아지지 않도록 하여 스레드 스케줄러가 고민할 일을 줄이는 것이다. 실행 준비가 된 스레드들이 맡은 작업을 완료할 때까지 계속 실행하게 만들면, 스레드 스케줄링 정책이 상이하더라도 동작이 크게 달라지지 않게 된다.

결국 실행 가능한 스레드 수를 적게 유지하는 것이 중요한데, 각 스레드가 작업을 완료 후엔 다음 작업 전까진 대기하도록 하는 것이 좋다. 실행자 프레임워크(Executor Framework)로 예를 들면, 스레드 풀 크기를 적절히 설정하고, 작업을 짧게 유지하면 된다.

2. 바쁜 대기 상태 회피

바쁜 대기(busy waiting) 상태는 공유 객체의 상태가 바뀔 때까지 권한을 얻기 위해 계속 검사하는 것을 말한다. 스레드가 바쁜 대기 상태에 빠지면, 스레드 스케줄러와 프로세서에 부담을 주게 되어 다른 작업이 수행될 기회를 줄여 성능을 저하시킬 수 있다.

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

위 코드는 기존 CountDownLatch 클래스를 바쁜 대기 상태로 구현한 것으로, 책에 따르면 10배 이상 느리다고 한다.

3. 스레드 우선순위를 조절하지 않기

Thread.yield를 사용하면 스레드 스케줄러에게 현재 스레드의 실행을 멈추고 다른 스레드에게 실행을 양보하도록 요청할 수 있다. 하지만 매번 같은 결과를 보장하지 않으며, OS에 따라 동작이 달라질 수 있기 때문에 사용하지 않는 것이 좋다.

Last updated

Was this helpful?