반응형

 Thread에서, stop, suspend, resume, destroy는 권장하지 않는 함수라는 것은 알고 계실 겁니다. 이 중에서, 오늘은 중간에 있는 두 메서드인 suspend랑 resume에 대한 이야기를 먼저 해 보도록 하겠습니다.

 

 

 먼저 이 그림은 stop을 할 때에도 그려놓고 있는 것이 좋겠습니다. 여기서 중요한 것은 resume의 대상이, suspend된 상태에서만 쓸 수 있습니다. 이는 링크에 나온 이 문구를 보면 대략적으로 알 수 있습니다.

 

 solely라는 말은, 단지라는 부사입니다. solo랑 비슷한 어감일지도 모르겠어요. 영어를 잘 못해서, 정말 그런지는 잘 모르겠습니다. 혹이 이에 대해 아시는 분 있으면 댓으로 알려주시면 감사하겠습니다. 어찌 되었던 문맥상, suspend를 썼을 때에만 쓴다는 것 정도는 감이 오실 거에요. 그러면, suspend 메서드가 권장하지 않는 함수라면, resume도 권장할 이유는 없을 거에요. 스택오버플로우에 올라온 질문에 있는 코드를 변형한 예제를 들고 오겠습니다. 코드가 설명하기에는 꽤 깔끔한 듯 싶어요.

 

 


 예제 코드를 봅시다.

 

 

 Thread를 상속한 Worker의 run 메서드는 위와 같습니다. CHOGAHUI05라는 문자열을 100번 출력합니다.

 

 

 다음에, main 함수에서는 w를 start를 시킵니다. 다음에, main 함수를 실행하고 있는 main Thread를 단위 시간 1동안 sleep 하게 한 다음에, w를 suspend를 시킵니다. 그리고, main Thread는 "CHOGAHUI IS MY DOG"를 출력하고, w를 다시 resume 시킵니다. 얼핏 보면, 꽤 정상적으로 작동할 거 같습니다.

 

 

 그랬다면, 위에 네모 아이콘이 회색이 되어야 했을 겁니다.

 

 

 그런데, 빨간색이 활성화 되어 있습니다. 심지어 "CHOGAHUI IS MY DOG" 라는 문자열은 출력이 되지도 않았습니다. 무슨 일이라도 생긴 것일까요? Main Thread가 진행이 되지 못할 이유가 뭐가 있었을까요? 일단, println을 내부를 보면, 아래와 같은 코드가 있음을 확인할 수 있습니다.

 

 

 805번째 줄의 synchronized(this). 이게 의미하는 바가 무엇인가요? 심지어 println은 static이 붙지 않았어요.

 

 

 이것은 Thread가 805번째 줄에 있는 block에 접근했을 때, PrintStream에 대한 LOCK을 획득한다는 의미입니다. 더 정확히 말하면, Thread Worker가 806번째 줄을 수행한다고 해 봅시다.

 

 

 이 때, Main이 805번째 줄에 접근하려고 한다면 어떨까요? 이미, Worker가 printStream 객체에 대한 Lock을 획득한 상태입니다. 그렇기 때문에, BLOCKED가 됩니다.

 

 

 즉, Main이 printStream에 대한 Lock을 취득할 수 있을 때, 805번째 줄에 있는 block에 접근 가능합니다.

 

 


 그런데, 이런 상황을 생각해 봅시다. 이 상황에서, Main이 Worker를 suspend 시켰습니다.

 

 

 이 때, Worker가 printStream에 대한 Lock을 풀었다면 별 문제가 되지 않았을 수도 있습니다. Main 입장에서는, 20번째 줄에 있는 "CHOGAHUI IS MY DOG" 라는 것을 출력하고 Worker를 resume 하면 되기 때문입니다. 그런데, Worker가 suspend 될 때, 동기화 블록 내에 있었고, 출력 스트림 객체에 대한 LOCK이 풀리지 않는다면, 이런 상황이 발생할 수 있습니다.

 

 

 현재 Worker는 suspend 된 상태입니다.

 

 

 다음에 Main은 20번째 줄을 수행합니다. 그런데, 이미, printStream 객체가 LOCK이 되어 있습니다. 따라서, Main은 BLOCK이 됩니다. 더 이상 진행을 할 수 없습니다.

 

 

 Main은 Worker에게, 너가 깨어나기를 원한다면 20번째 줄을 실행하게 하면 된다를 요구하고 있습니다. 그런데, 그렇게 하기 위해서는, Worker가 가지고 있는 printStream의 Lock이 필요합니다.

 

 

 대충 이런 상황입니다. Main은 printStream lock이 필요하고, lock은 worker가 점유하고 있어요. 그런데, worker가 lock을 풀려면, Main이 w.resume(). 즉 21번째 줄을 수행해야 합니다. 이걸 간단하게 바꾸면, Main은 Worker를 resume을 할 수 있는 '권한'을 가지고 있어요. 그것이 Worker 입장에서는 필요한 것입니다.

 

 

 그런데 Worker는 너가 resume을 나에게 줄 때 까지 lock을 안 줄래. 라고 하고 있고. Main은 너가 lock을 주지 않으면 나도 resume을 실행하지 못할 거 같아. 라고 이야기를 합니다. 어떤 상황인지 이해가 되실 거에요. 문서에서 데드락이 발생하기 쉬워서 권장하지 않는 함수가 되었다는 이야기가 나오는데요. 어느 과정을 실행할 때, suspend가 될 지 모르고, resume으로 RUNNABLE로 만들 수 있어요. 그런데, 때에 따라서, (엉뚱하게도) A는 B에게 자원 1을 주기 위해 자원 2가 필요한데, B는 A에게 자원 2를 주기 위해 자원 1이 필요한 관계가 만들어질 수 있어서, 그런 듯 싶습니다.

 

 그러면, 이 경우는 어떤가요?

 

 

 이 경우에는 아래와 같이 그림이 그려집니다.

 

 

 최소한 Main은 w.suspend()를 호출하고 나서, w.resume()을 호출하기 위해서 worker가 가지고 있는 LOCK이 필요하지 않습니다. 위와 다른 상황인 거 눈치 채셨으리라 생각이 듭니다.

 

반응형

댓글을 달아 주세요

  1. Deborah

    코딩을 오늘도 열심히 배워 봅니다.

  2. ㄲ ㅏ누

    오늘은 특히 더 어렵네요 ㅠ
    어려운 포스팅 잘 보고 갑니다 ㅎㅎ
    쉬운것도 자주 부탁드려요
    코딩강아지님 ㅎ

  3. 상식체온

    "이 방법은 suspend( )와 함께 사용하는 데만, (또는 단독으로) 존재한다"????
    suspend만 시키면 될 것 같은데 또 resume 까지 쓰는 군요. 어렵습니다....

    • 코딩강아지
      2020.02.11 22:06 신고

      suspend 된 스레드를 깨우기 위해서는
      단지 resume을 호출하는 것 뿐이다.
      일 거에요.

      단지 suspend는 '일시 정지'된 것이니
      어떻게든 끝내려면 '다시 시작'이 되어야 하니..