Thread 첫 포스팅을 하고 바빠서 공부는 했지만 포스팅을 못했습니다. 제가 돌아왔습니다!
업무를 하다보면 Thread에 관련된 코드를 보면 하나도 모르는 이유 무엇일까 생각해보니 Thread에 대해서 제대로 공부한 적이 없었던 거에요!
이제부터 Thread! 이 녀석의 매력을 파헤쳐 보겠습니다!
쓰레드를 생성했다고 해서 자동으로 실행되지는 않습니다. start( ) 메서드를 호출해서 쓰레드를 실행 할 수 있는데요. 여기에는 또 한가지 숨겨진 비밀이 있습니다.
start( )가 호출되었다고 해서 바로 실행되는 것이 아닙니다.
실행대기 상태에 있다가 자신의 차례가 되어야 실행이 되는 것입니다. 당연한 이야기겠지만 실행 대기중인 쓰레드가 하나도 없다면 곧바로 실행상태가 됩니다.
🔥 쓰레드의 실행순서는 OS의 스케쥴러가 작성한 스케쥴에 의해 결정됩니다.
여기서 또 중요한 포인트가 있는데요!
Stream, Iterator, Thread의 공통점이 있습니다. 서로 너무 다른 것 같지만 이렇게 연계해서 공부하니까 서로 공통점을 이용해 같이 개념을 외우기가 좋았습니다.
Stream은 한번 최종 연산을 하면 닫히기 때문에 더 이상 사용할 수 없습니다. 닫힌 스트림을 이용해 무언가를 하려고 시도하면 예외가 발생했던 거 기억 나시죠?
기억 하고 계실거라 믿겠습니다..
Iterator 또한 한번 얻어서 안에 담겨진 모든 요소를 순회하고 나면, 재사용이 불가능해서 다시 Iterator가 필요한 경우에는 새로 얻어와서 사용해야 했습니다.
왜 Stream과 Iterator 이야기를 했을까요? 그저 제 억지였을까요?
물론 아닙니다. 제가 봤을때 Stream은 한번 닫히면 사용하지 못하고, Iterator도 모든 요소를 순회하면 다시 사용이 불가능합니다. Thread도 start( )를 호출해 실행되어 실행이 종료되면 다시 실행할 수 없습니다. 즉 start( ) 메서드는 Thread를 생성한 이후 한 번만 실행이 가능합니다.
만약 start( )를 두번 이상 호출할 경우에는 IllegalThreadStateException이 발생하게 됩니다.
쓰레드를 실행 시킬때 왜 run( )을 호출하지 않을까 하는 의문이 드셨을 수 있습니다.
그렇다면 run( )을 실행 시킨것과 start( )를 실행시킨 것의 차이에 대해 알아보겠습니다.
main 메서드에서 run( )을 호출하게 되면 생성된 쓰레드를 실행시키는 것이 아니라, 단순히 클래스에 선언된 메서드를 호출하는 일만 수행합니다.
run( )을 호출하면 Call Stack(호출 스택)이 어떻게 변화하는지 그림으로 살펴 보겠습니다.
호출 스택에 run( )이 첫 번째로 올라가게 하는 것을 볼 수 있습니다.
start( )에 대해서 알아보기 전에, 쓰레드에 대해서 조금 더 알아보겠습니다.
모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 합니다. 따라서 run( )을 호출하는 것만으로는 호출 스택이 생성되지 않기 때문에 쓰레드가 실행되지 않는 것입니다.
start( )를 호출하게 되면 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성하고 run( )을 호출해서 생성된 호출스택에 run( )이 첫 번째로 올라가게 합니다.
즉, 새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 작업에 사용된 호출스택은 소멸된다는 뜻입니다.
새로운 쓰레드를 생성하고 start( )를 호출할때 일어나는 호출 스택의 변화를 살펴보겠습니다.
각 그림에 대한 설명은 아래와 같습니다.
1. main 메서드에서 쓰레드의 start( ) 를 호출합니다.
2. start( )는 새로운 쓰레드를 생성하고, 쓰레드가 작업하는데 사용할 호출 스택을 생성해줍니다.
3. 새로 생성된 호출스택에 run( )이 호출되어, 쓰레드가 독립된 공간에서 작업을 수행합니다.
4. 호출 스택이 2개이므로 스케쥴러가 정한 순서에 의해서 번갈아 가면서 실행됩니다.
호출 스택에서는 가장 위에 있는 메서드가 현재 실행중인 메서드이고 나머지 메서드들은 대기상태에 있다고 알고 계실겁니다. 호출 스택이 하나라면 이것이 맞습니다.
그러나 쓰레드가 둘 이상이면, 호출 스택이 둘 이상이라는 이야기가 됩니다.
쓰레드가 둘 이상일 경우에는 호출스택의 최상위에 있는 메서드일지라도 대기상태일 수 있습니다.
스케줄러는 실행대기중인 쓰레드들의 우선순위를 고려하여 실행순서와 실행시간을 결정하기 때문입니다. Thread의 우선순위를 지정할 수 있지만, OS 스케줄러는 우선순위가 낮은 쓰레드를 먼저 실행 시킬 수도 있습니다. 자세한 내용은 뒤에서 알아보겠습니다.
결국 main 메서드의 작업을 수행하는 것도 쓰레드이고, 우리의 작업을 하나하나 실행하는 모든 것이 쓰레드 입니다. 따라서 프로그램이 종료되었다는 것은, 실행 중인 사용자 쓰레드가 하나도 없다는 뜻이 됩니다. 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료됩니다.