티스토리 뷰

donaricano-btn
반응형

멀티스레드 환경이라는 것은 다시말해 호출 스택이 여러개 존재하는 것이다 라는 이야기를 지난번에 했었습니다. 하지만 실제로 이렇게 만들어진 다중 스택들은 동시에 작업을 수행하는 것이 아니고 싱글 코어 환경이거나 혹은 스레드의 숫자가 코어의 숫자보다 더 많은 경우에는 스레드 스케줄러에 의해서 순차적으로 번갈아 가며 작업이 수행되게 됩니다.


JVM은 우선순위(Priority)방식과 순환할당(Round-Robin)방식을 사용해 스레드 스케줄링을 합니다. 우선순위 방식은 우선순위가 높은 스레드에 더 많은 시간을 할당하는 방법이고, 순환할당 방식은 각각의 스레드에 정해진 시간만큼을 할당하고 돌아가면서 수행하는 것을 의미합니다.


우선순위 방식을 통해 해당하는 스레드의 할당 시간을 조절하려면 Thread 클래스에 있는 setPriority() 메소드를 이용하면 됩니다.


스레드 객체를 생성한 뒤 setPriority() 메소드를 호출하고 우선순위를 인자값으로 넘기면 그 우선순위에 따라 시간 할당이 됩니다. 우선순위는 1에서부터 10까지의 정수로 정해지게 되고 숫자가 클수록 높은 우선순위를 가지게 됩니다. 코드의 가독성을 높이기 위해 Thread 클래스에 있는 상수값을 사용할 수 있습니다.


하지만 우선순위를 조절하는 것은 스레드의 할당 시간을 조절하는 것이고 순서와는 관계가 없습니다. 따라서 개발자는 스레드의 실행 순서를 전혀 예측할 수 없고 스레드의 실행 순서가 언제나 같을 것이라고 생각해서는 안됩니다. 그렇기 때문에 완전히 비동기적으로 작업이 수행될 수 있는 경우에만 멀티스레드 방식을 사용하는 것이 좋습니다. 하지만 때로는 스레드의 실행 순서를 제어해야만 할 때도 있습니다.


우선 스레드의 생명 주기에 대해 잠깐 살펴보겠습니다.  


  1. 스레드 객체를 생성하고 start() 메소드를 호출하기 전 까지는 스레드가 생성되지 않는다는 걸 지난 시간에 말씀 드렸는데, start()메소드를 호출한다고 해서 바로 스레드가 실행되는 것 역시 아닙니다. 먼저 실행대기열에 저장되어 자신의 차례가 될 때까지 기다려야 합니다. 실행대기열은 queue로 이루어져 있고 따라서 쓰레드는 선입선출의 구조를 가지고 있습니다.

  2. 실행대기열에서 실행대기 상태에 있던 스레드가 자신의 차례가 되면 실행됩니다.

  3. 주어진 실행시간이 다 되거나 yeild() 를 만나게 되면 다시 실행 대기 상태가 되고 다음 차례의 스레드가 실행됩니다.

  4. 실행 중에 suspend(), sleep(), wait(), join(), I/O block(입출력 작업에서 발생하는 지연상태, 사용자의 입력을 기다리는 경우 일시정지 상태에 있다가 사용자가 입력을 마치면 다시 실행대기상태가 된다)에 의해 일시정지 상태가 일어날 수 있습니다.

  5. 지정된 일시정지시간이 다 되거나(time-out), notify(), resume(), interrupt()가 호출되면 일시정지 상태를 벗어나 나시 실행대기열에 저장되어 자신의 차례를 기다리게 됩니다.

  6. 실행을 모두 마치거나 stop() 가 호출되면 스레드는 소멸됩니다.


여기서 지난시간에 이야기 했던 sleep() 외에도 스레드를 일시정지시키거나 대기열로 돌려보내는 메소드들이 있다는 것을 알 수 있습니다. 이 메소드 들을 이용하면 스레드의 실행을 좀 더 세밀하게 제어할 수 있습니다. 이 메소드들에 대해 살펴보도록 하겠습니다.


이것은 스레드의 생명주기에 따라 스레드가 가지게 되는 상태를 설명한 표입니다. Thread클래스에 getState()를 통해서 해당하는 열거상수를 얻을 수 있습니다.(Thread.State)


NEW는 스레드 객체가 생성된 단계입니다. 이 때는 아직 start()가 호출되지 않은 상태입니다. RUNNABLE start()가 호출되고 스레드가 실행대기열에서 대기중인 상태를 의미합니다. WAITING은 어떤 메소드를 만나서 잠깐 다른스레드가 통지할 때 까지 일시정지중인 상태를 의미하고 TIMED_WAITING은 주어진 시간동안 기다리는 상태를 의미합니다. BLOCKED는 사용하고자하는 객체의 락이 풀릴 때까지 기다리는 상태를 의미합니다. 그리고 TERMINATED는 스레드의 종료를 의미합니다.


메소드의 종류를 알아보면 먼저, 지난시간에 알아본 sleep()이 있습니다. 지정된 시간동안 스레드를 일시정지시킵니다. join() 메서드는 반대로 호출한 스레드를 일시정지 시킵니다. 이렇게 일시정지 상태인 스레드에 interrupt() 메소드를 호출하게 되면 InterruptedException을 발생시켜 실행대기상태로 가거나 종료시킬 수 있습니다.  yield() 메소드는 다른 스레드에 순서를 양보하게 만드는 메소드 입니다. (suspend(), resume(), stop() 는 deprecated 되었다)


그리고 Thread 클래스가 아니라 Object 클래스 내에 있는 메소드 중에 wait() 메소드가 있습니다. 역시 스레드를 일시정지상태로 만들고 notify() 메소드를 호출해 실행대기상태로 되돌리게 됩니다.


그러면 이런 메서드의 실제 사용 예를 간단하게 살펴보고 스레드 파트를 마치도록 하겠습니다.


위 코드에서 join() 메서드를 호출하면 호출한 메인 스레드가 해당 스레드의 종료까지 대기하게 됩니다.  그런데 해당 스레드에 InterruptedException이 발생하게 되면 다시 메인 스레드가 대기상태로 돌아오게 됩니다. 그렇지 않으면 위 코드에 두 개의 스레드는 차례대로 호출되기 때문에 스레드의 실행순서에 개입할 여지가 생기게 됩니다.




wait()와 notify() 메소드의 예도 보도록 하겠습니다. wait()와 notify()의 경우 동기화된 블럭 안에서만 사용해야 합니다. wait() 메소드는 동기화된 공유자원에 접근할 때 동기화된 공유자원을 다른 스레드에서 사용하고 있다면 기다리라는 신호입니다. 그리고 이 자원을 기다리고 있는 스레드에게 사용이 끝났음을 알려주는 것이 notify() 입니다. 단순히 synchronized활용한 것 보다 조금 더 미세하게 제어를 수행할 수 있게 됩니다.(락을 가지고 있다면 락을 내려놓고 기다리게 됩니다)


오른쪽 메인 스레드에서 ThreadB 객체를 생성하고 start() 메소드를 호출한 뒤 wait() 메소드를 호출하면 b 스레드에서 notify()를 호출할 때 까지 기다리게 됩니다.


이상으로 스레드에 관한 내용을 마치겠습니다.

반응형

'language > Servlet&JSP' 카테고리의 다른 글

redirect / forward  (0) 2019.02.22
서블릿(servlet) 생명주기(lifecycle)  (0) 2019.02.22
JSP 동작 순서와 MVC 패턴 동작  (0) 2019.02.15
서블릿(Servlet)의 동작구조  (1) 2019.02.15
HTTP 프로토콜 요청/응답  (0) 2019.02.15
donaricano-btn
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함