안녕하세요. chogahui05입니다. 생산자 소비자 문제를 구현할 때, 한 가지 의문이 들었습니다. 분명히 맞게 구현한 거 같은데 왜 제대로 동작을 하지 않을까? 이는 제가 쓰레드를 시작할 때, start 메서드를 불러야 하는데, run 메서드를 불렀었기 때문입니다. 그렇기 때문에 LOCK을 풀어야 하는데 풀리지도 않고. 계속 wait 상태에만 걸렸었습니다. notify가 들어가지도 않고. 3시간을 날린 것은 덤이였고요. 그런 걸 보면, 전 면접에도, 서류도 통과할 자격이 없는 건 분명해 보입니다. 이 둘이 어떤 차이가 있길래 그럴까요?

 

 


 Thread 클래스의 내부를 간단하게 보겠습니다.

 

 여러 가지 쓰레드에 대한 정보들이 있는데요. 이 중, group이라는 것도 볼 필요가 있습니다. 713번째 줄에, ThreadGroup의 add가 호출이 되기 때문입니다.

 

 

 start 메소드가 다소 길기 때문에, 2개 부분으로 쪼개겠습니다. 먼저, threadStatus가 0이 아닌 경우, illegal 예외를 발생시킵니다. 0 status는, New 상태를 의미하는데요. 만들어 진 상태입니다. 5상태에 대해서 이야기를 했을 텐데요. New, Runnable, Run, 대기, destroy. 이렇게 있다고 했어요.

 

 

 그러면, start()가 하는 역할은 간단하게 말해서, NEW 상태에 있는 쓰레드를 RUNNABLE한 상태로 되게 해 준다고 했어요. 그러면 실행 가능한 상태라는 것은 무엇인가요? 실행될 수 있는 대기 큐에 들어간 것을 의미합니다.

 

 

 그림으로 그리면 대략 이런 상태입니다. 이 상태에서, 스케쥴러가 RUN 할 쓰레드를 선택해서 실행시키면 될 거에요. 그러면, 이러한 큐에 들어가기 위해서 무엇이 필요할까요? 메타 데이터들이 필요합니다. 어느 그룹에 속해있는지, 우선 순위는 어떻게 되는지, 기타 등등.

 

 

 이러한 것들을 특정한 자료구조에 넣습니다. 조금 더 눈치를 채셨다면, 그 구조는 동적 배열임을 알 수 있어요.

 

 

 그 다음에, start0() 메소드를 호출하는데요. 이것은 native 메소드로 선언이 되어 있습니다. 어찌 되었던, 생성한 Thread의 정보를 넣은 다음에, 어딘가에 전달을 할 겁니다. 이러한 일련의 과정들이 수행된 다음에, override를 한 run 메소드가 호출이 됩니다.

 

 

 실제로 run()은, 현재 쓰레드가 RUN이 될 때, 실행해야 하는 코드들을 담고 있습니다. 이 코드는 아래와 같습니다.

 

 

 

 현재 run을 실행하고 있는 Thread의 이름을 출력합니다.

 


 그러면, 이 경우 실행 결과는 어떻게 나올까요? start를 부르고, 또 다시 start를 부르고 있어요. 그러면, 이미 RUNNABLE이나, RUN 상태인 Thread에, 또 start를 호출합니다. 그러면 illegal 할 겁니다. 따라서, exception을 출력할 수도 있겠네요.

 

 

 정말 그렇네요. 그러면 a.start()도 1번, b.start()도 한 번 호출하면 어떻게 될까요? 

 

 

 즉, 저는 코드를 위와 같이 바꾸었습니다.

 

 정보가 제대로 들어왔네요. Threa의 start는, 생성한 일꾼의 메타 정보를 넘긴 후에, override된 run을 호출한다. 해당 포스트를 단 1줄로 압축한 문장입니다. 그러면, 그냥 run만 호출하면 어떻게 될까요?

 

 

 

 

 사실 Thread run만 호출했다면, 메타 정보를 어딘가에 넣고, 새로 생성한 Worker가 RUN 할 수 있게 만드는 과정은 생략이 될 겁니다. 그러면 어떻게 되는지 그림으로 그려 봅시다.

 

 

12, 13번째 줄을 수행하고 나면, Thread를 extend한 Worker 객체 a와 b가 생성이 되기는 했습니다.

 

 

 그런데, 실제로 New, 생성이 되기만 했지, 그것의 메타 데이터들이 들어가지 않았습니다. RUNNABLE 상태가 아니기 때문에, 실행 대기 큐에 worker a에 대한 정보와 b에 대한 정보가 들어가지 않습니다.

 

 

 그러면 이것을 수행하면 어떻게 출력될까요? a.run()이 돌아갈 때, main이 출력될까요? 쓰레드 a의 이름이 출력될까요?

 

 

 Thread의 start 메소드를 실행하지 않았기 때문에, a, b의 메타 정보가 들어가지 않았을 겁니다. 그러면 a에 대한 정보, b에 대한 정보를 제대로 얻어오지 못할 겁니다. 당연하게도, 그냥 override 된 method만 호출을 했을 뿐입니다. worker에 대한 메타 정보가 들어가지 않았기 때문에, notify를 해도, 원하는 대로 동작하지 못할 가능성이 클 거에요.

 

 이 두 함수는 저만 그러겠지만, 상당히 혼동되기 쉬운 함수 중 하나입니다. 헷갈리신다면, start는 해당 쓰레드를 new에서, run이 가능한 상태로 만들어 준다. 정도만 기억하셔도 충분할 듯 싶습니다.