멀티스레드와 동시성

Ch04. 스레드 제어와 생명 주기2 - yield

webmaster 2024. 8. 8. 08:57
728x90

어떤 스레드를 얼마나 실행할지는 운영체제가 스케줄링을 통해 결정한다.
그런데 특정 스레드가 크게 바쁘지 않은 상황 이어서 다른 스레드에 CPU 실행 기회를 양보하고 싶을 수 있다. 이렇게 양보하면 스케줄링 큐에 대기 중인 다른 스레드 가 CPU 실행 기회를 더 빨리 얻을 수 있다.

Empty(아무것도 적용 X)

실행 코드
실행 결과
  • 1000개의 스레드를 실행한다.
    • 각 스레드가 실행하는 로직은 아주 단순하다. 스레드당 0~9까지 출력하면 끝난다.
  • sleep(1) , yield() 없이 호출한다. 운영체제의 스레드 스케줄링을 따른다. 
  • 특정 스레드가 ~ 수행된 다음에 다른 스레드가 수행되는 것을 확인할 있다.
  • 참고로 실행 환경에 따라 결과는 달라질 있다. 다른 예시보다 상대적으로 하나의 스레드가 ~ 연달아 실행되 다가 다른 스레드로 넘어간다.
  • 부분은 운영체제의 스케줄링 정책과 환경에 따라 다르지만 대략 0.01(10ms)정도 하나의 스레드가 실행되 , 다른 스레드로 넘어간다.

Sleep 적용

실행 코드
실행 결과

  • sleep(1) : 특정 스레드를 잠시 쉬게 한다
  • sleep(1) 을 사용해서 스레드의 상태를 1밀리초 동안 아주 잠깐 "RUNNABLE" -> "TIMED_WAITING" 으로변경 한다. 이렇게 되면 스레드는 CPU 자원을 사용하지 않고, 실행 스케줄링에서 잠시 제외된다. 1 밀리초의 대기 시간 이후 다시 "TIMED_WAITING " -> "RUNNABLE" 상태가 되면서 실행 스케줄링에 포함된다.
  • 결과적으로 "TIMED_WAITING" 상태가 되면서 다른 스레드에 실행을 양보하게 된다. 그리고 스캐줄링 큐에 대기 중인 다른 스레드가 CPU 실행 기회를 빨리 얻을 있다.
  • 하지만 이 방식은 "RUNNABLE" -> "TIMED_WAITING" -> "RUNNABLE"로 변경되는 복잡한 과정을 거치고, 또 특정시간 만큼 스레드가 실행되지 않는 단점이 있다.
    • 예를 들어서 양보할 스레드가 없다면, 차라리 나의 스레드를 실행하는 것이 나은 선택일 있다. 방법은 나머지 스레드가 모두 대기 상태로 쉬고 있어도 스레드까지 잠깐 실행되지 않는 것이다. 쉽게 이야기해서 양보할 사람이 는데 혼자서 양보한 이상한 상황이 있다.

yield 적용

실행 코드
실행 결과
  • 자바의 스레드가 RUNNABLE 상태일 때, 운영체제의 스케줄링은 다음과 같은 상태들을 가질 수 있다.
    • 실행 상태(Running): 스레드가 CPU에서 실제로 실행 중이다.
    • 실행 대기 상태(Ready): 스레드가 실행될 준비가 되었지만, CPU가 바빠서 스케줄링 큐에서 대기 중이다.
    • 운영체제는 실행 상태의 스레드들을 잠깐만 실행하고 실행 대기 상태로 만든다. 그리고 실행 대기 상태의 스레드들을 깐만 실행 상태로 변경해서 실행한다. 과정을 계속 반복한다. 참고로 자바에서는 상태를 구분할 수는 없다.
  • Thread.yield() 메서드는 현재 실행 중인 스레드가 자발적으로 CPU를 양보하여 다른 스레드가 실행될 수 있도록 한다.
  • yield() 메서드를 호출한 스레드는 RUNNABLE 상태를 유지하면서 CPU를 양보한다. 즉, 이 스레드는 다시 스케줄링 큐에 들어가면서 다른 스레드에게 CPU 사용 기회를 넘긴다.

자바에서 Thread.yield() 메서드를 호출하면 현재 실행 중인 스레드가 CPU를 양보하도록 힌트를 준다. 이는 스레드가 자신에게 할당된 실행 시간을 포기하고 다른 스레드에게 실행 기회를 주도록 한다. 참고로 yield()는 운영체제의 스케줄러에게 단지 힌트를 제공할 뿐, 강제적인 실행 순서를 지정하지 않는다. 그리고 반드시 다른 스레드가 실행되는 것도 아니다.

 

yield()는 "RUNNABLE" 상태를 유지하기 때문에, 쉽게 이야기해서 양보할 사람이 없다면 본인 스레드가 계속 실행될 수 있다.

 

참고로 최근에는 10 코어 이상의 CPU도 많기 때문에 스레드 10개 정도만 만들어서 실행하면, 양보가 크게 의미가 없다. 양보해도 CPU 코어가 남기 때문에 양보하지 않고 계속 수행될 수 있다. CPU 코어 수 이상의 스레드를 만들어야 양 보하는 상황을 확인할 수 있다. 그래서 이번 예제에서 1000개의 스레드를 실행한 것이다.

 

참고: log() 사용하는 기능은 현재 시간도 획득해야 하고, 날짜 포멧도 지정해야 하는 복잡하다. 사이에 스레드의 컨텍스트 스위칭이 발생하기 쉽다. 이런 이유로 스레드의 실행 순서를 일정하게 출력하기 어렵다. 그래 여기서는 단순한System.out.println()을사용했다.

728x90