[Effective Java] ITEM84 : 프로그램의 동작을 스레드 스케줄러에 기대지 말라
스레드 스케줄러와 Thread.yield, 스레드 우선순위에 의존하지 말자. 견고성과 이식성을 모두 해치는 행위이다.
스레드 우선순위는 프로그램의 서비스 품질을 높이는데 드물게 쓰일 수는 있지만 프로그램을 고치는 용도로 사용해서는 안된다.
• 정확성이나 성능이 운영체제 고유의 스레드 스케줄러에 따라 달라지는 프로그램일수록 다른 플랫폼에 이식하기 어렵다.
• 견고하고 이식성 좋은 프로그램을 작성하기 위해서 고려해야 할 것들
◦ 실행 가능한 스레드의 평균 개수를 프로세서 수 보다 지나치게 많아지지 않도록 관리할 것
◦ 즉 실행가능한 스레드 수를 가능한 적게 유지하는 것이 중요하다.
실행 가능한 스레드 수를 적게 유지하는 방법
• 실행 준비가 된 스레드들은 맡은 작업을 완료할 때 까지 계속 실행되도록 할 것
◦ 단 실행 가능한 스레드 수 와 전체 스레드 수는 구분해야 한다. (대기 중인 스레드 = 실행 불가)
• 스레드는 당장 처리해야 할 작업이 없다면 실행 되어서는 안된다.
• 스레드는 Busy Waiting 상태가 되면 안된다.
◦ Busy Waiting 상태

- 스레드 스케줄러 환경의 변화에 취약하다. (이식성이 낮다)
- 프로세서에 부담을 주어 다른 작업이 실행의 기회를 박탈당한다. (성능이 낮다)
- 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--;
}
}
Thread.yield 사용을 주의하라
• Thread.yield : 다른 스레드에게 실행을 양보하는 메서드
◦ 실행 중인 스레드를 RUNNABLE (실행 대기) 상태로 바꾼다.
- 스레드가 실행되었는데, 그 실행이 잠시동안 무의미한 경우가 있을 수 있다.
- 이런 경우 스레드를 잠시 실행 대기 상태로 돌려놓으면 CPU의 자원의 소모를 방지할 수 있다.
- 이 때 Thread.yield 메소드가 유용하게 사용될 수 있다.
• 특정 스레드가 다른 스레드에 비해 CPU 자원을 할당받지 못하는 경우에도 Thread.yield 사용은 지양하자
◦ 증상이 호전될 수 있어도 여전히 이식성이 나쁘다
- JVM 종류에 따라 차이가 미미할수도, 악화될수도 있다.
• 차라리 어플리케이션 구조를 바꿔 동시에 실행 가능한 스레드 수를 줄이는 것이 좋다.
스레드 우선순위 조정에 주의하라
• 스레드 우선순위는 자바에서 이식성이 가장 나쁜 특성 중 하나이다.
◦ A 타입의 JVM 에서 1-2-3 의 순서로 실행되었다고 하더라도, B 타입의 JVM에서 동일한 순서로 실행되리라는 보장이 없다.
- A 타입에서 1-2-3 의 순서로 실행된 것이 B 타입의 JVM 에서는 3-1-2 의 순서로 실행될 수 있음
- 즉 1-2-3 이 최적의 성능을 보장하는 우선순위 였다고 해도, 다른 JVM 에서 3-1-2 의 순서로 실행되어 버리면 다시 성능이 나빠진다.
• 특히 응답 불가 문제는 스레드 우선순위로 해결해선 안된다.
◦ 진짜 원인을 찾아 수정하기 전까지 같은 문제가 반복해서 발생할 것이다.