핵심 요약
- 스레드 스케줄러 동작은 플랫폼마다 다를 수 있으므로, 프로그램이 스레드 스케줄링 정책에 의존하면 안 된다.
- Thread.yield나 스레드 우선순위 조정같은 스케줄러 관련 기능에 의존하는 것은 이식성을 해칠 수 있으며, 신뢰성을 낮춘다.
- 올바른 동시성 제어나 프로그램 설계를 통해 동시에 실행 가능한 스레스 수를 최소화하는 것이 좋다.
자바 스레드 생명 주기

정확성이나 성능이 스레드 스케줄러에 따라 달라지는 프로그램이라면 다른 플랫폼에 이식하기 어렵다.
- 여러 스레드가 실행 중이면 운영체제의 스레드 스케줄러가 어떤 스레드를 얼마나 오래 실행할지 결정하는데, 구체적 스케줄링 정책은 운영체제마다 다를 수 있다.
- 프로그램이 스레드 스케줄링 정책에 의존적이라면, 해당 프로그램의 동작이나 성능이 운영체제마다 다르게 나타날 수 있다. 이러한 프로그램은 다른 플랫폼에 이식이 어렵다.
프로그램의 성능과 이식성이 높은 프로그램을 작성하는 방법
✍ 실행 가능한 스레드의 평균적인 수를 프로세서 수보다 지나치게 많아지지 않도록 하자.
- 멀티 스레딩 환경에서 스레드는 프로세서에서 실행된다.
- 만약 실행 가능한 스레드의 수가 프로세서 수보다 훨씬 많으면, 각 스레드는 자주 실행 중단되고 다른 스레드가 실행되기 시작한다.
- 따라서 문맥 교환과 스레드 간 경합으로 인해 프로세서의 자원 낭비와 성능 저하가 발생할 수 있다.
- 전체 스레드 수가 아닌 실행 가능한 스레드 수임을 유의하자.
실행 가능한 스레드 수를 적게 유지하는 방법
✍ 스레드가 작업을 완료한 후 대기 상태로 전환되어야 한다.
- 스레드는 작업을 완료한 후 즉시 다른 작업을 수행하려고 하기보다는 대기 상태로 전환돼야 한다.
- 이렇게 함으로써, CPU의 리소스를 효율적으로 활용할 수 있다.
- 작업이 없는 스레드가 계속 실행 상태로 있으면, 불필요한 문맥 교환과 CPU 자원의 낭비가 심해지게 된다.
- ex. 실행자 프레임워크는 스레드 풀 크기를 적절히 설정하고 작업은 짧게 유지한다.
- 단, 너무 짧으면 작업 분배 부담이 오히려 성능을 떨어뜨린다.
- +) Java의 실행자 프레임워크는 작업을 스레드에 분배하는 정책, 스레드 풀의 크기, 스레드의 생명주기 등을 적절히 설정하여 시스템의 성능을 최적화할 수 있다.
✍ 스레드는 절대 바쁜 대기 상태가 되면 안 된다.
❔ 바쁜 대기
더보기
- 스레드가 특정 조건이 충족될 때까지 계속해서 해당 조건을 검사하면서 대기하는 것을 말한다.
- 바쁜 대기 중인 스레드는 CPU 시간을 많이 차지하게 된다.
- 따라서 다른 스레드들이 실행될 기회가 줄어들게 되고, 전체 시스템의 성능이 저하될 수 있다.
- 바쁜 대기 상태의 스레드는 스레드 스케줄러의 동작을 예측하기 어려워, 원하는 조건의 충족 시점을 놓칠 수 있으며, 이는 전체적인 성능과 동작의 예측성을 저하시킬 수 있다.
예시 1 - 끔찍한 CountDownLatch 구현(바쁜 대기 버전)
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--;
}
}
더보기
- await 메소드는 바쁜 대기로 구현되어 있어, 대기 중인 스레드들이 CPU 시간을 빈번하게 요청한다.
- 이로 인해 다른 스레드들이 CPU를 사용하는 기회가 줄어들고 전체 시스템의 성능이 저하될 수 있다.
추가로 주의해야 할점
✍ Thread.yield을 사용해서 문제를 해결하려고 하지 마라.
- Thread.yield는 현재 실행 중인 스레드가 실행 중단을 원할 때 JVM에게 그 의도를 표현하는 메소드이다.
- 실제로 실행이 중단되거나 다른 스레드가 실행될 것이라는 보장은 없다.
- 스레드가 CPU 시간을 충분히 얻지 못할 때 Thread.yield를 사용하여 해결하는 것은 짧은 시간 동안만 효과가 있을 수 있으며 장기적으로는 문제를 해결하지 못한다.
- Thread.yield는 예측 불가능한 동작을 유발하여 프로그램의 안정성과 일관성을 해칠 수 있다. 즉, 이식성이 떨어진다.
- JVM 버전, 하드웨어, OS에 따라 오히려 성능 저하를 가져올 수 있다.
- 따라서 프로그램의 구조를 개선하여 스레드의 수를 최적화하는 것이 더 바람직하다.
✍ 스레드 우선순위를 조절하려 해결하려고 하지 마라.
- 스레드 안전순위 조정을 통한 성능 향상은 특정 환경에서만 작동할 수 있고, 다양한 환경에서의 안정적인 성능을 보장하지 못한다.
'Language > Java' 카테고리의 다른 글
| [모던 자바 인 액션] Ch 1 자바 8, 9, 10, 11 : 무슨 일이 일어나고 있는가? (0) | 2024.04.10 |
|---|---|
| [effective java] 아이템 83. 지연 초기화는 신중히 사용하라. (0) | 2023.08.13 |
| [effective java] 아이템 82. 스레드 안전성 수준을 문서화하라. (0) | 2023.08.13 |
| [effective java] 아이템 81. wait와 notify보다는 동시성 유틸리티를 애용하라. (0) | 2023.08.13 |
| [effective java] 아이템 80. 스레드보다는 실행자, 태스크, 스트림을 애용하라. (0) | 2023.08.13 |