ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Thread(스레드)의 Life Cycle(생명 주기), 그리고 관련 메소드
    JAVA 2011. 10. 2. 17:04
    스레드는 생성되어 소멸될 때까지 여러 형태의 생명주기를 가진다.

    스레드가 생성된 후의 상태는 크게 alived와 dead의 두 가지로 나누어진다. dead 상태는 스레드가 자신의 run() 메소드를 완전히 수행하여 더 수행할 코드가 남아 있지 않거나 stop() 메소드에 의하여 종료되는 경우이다. 나머지 모든 상태는 alive 상태인데 이 상태는 실행 가능 상태, 실행 상태, 대기 상태로 나눌 수 있다.

    실행 상태 - 스레드가 CPU를 차지하여 코드를 수행하는 단계
    실행 가능 상태 - Runnable pool이라고 하는 특정 장소에 실행 상태로 들어가기 위하여 스레드들이 모여있는 모습
    대기 상태 - sleep pool, wait pool, join pool, I/O blocking pool 등

    대기 상태에 있는 모든 스레드는 특정 조건을 만족하면 실행 가능 상태로 바뀌며 스케줄러를 통하여 실행 상태로 갈 수 있다.

    <스레드의 라이프 사이클>
     
    실행 가능 상태는 스레드가 CPU를 차지하여 실행 상태로 갈 수 있는 상황이다. 실행 가능한 스레드들이 모여서 대기하는 장소를 Runnable Pool이라고 한다. 이곳에는 수많은 스레드들이 모여 서로 경쟁적으로 CPU를 차지하고 실행 상태로 들어갈 기회를 엿보고 있다. 실행 상태로 들어갈 수있는 스레드는 1개이므로 나머지 스레드는 계속 Runnable Pool에서 대기해야 한다.
    실행 상태로 들어갈 수 있는 스레드를 선택하는 역할을 수행하는 것이 바로 스케줄러이다. 스케줄러는 JVM 안에서 수행되는 특별한 스레드라고 생각할 수 있다.


    스레드의 라이프 사이클을 보면 실행 상태에서 대기 상태로 나오는 여러 가지 상황이 설정되어 있는데 각 상황별로 적용되는 메소드가 다르다.

    sleep() 메소드
    sleep() 메소드는 현재 실행중인 스레드를 Sleep Pool로 쫒아낸다. sleep() 메소드는 밀리 세컨드 단위로 표현된 인자를 가질 수 있는데, 이 시간동안 Sleep Pool에서 대기한다. 시간이 모두 경과되거나 사용자가 인터럽트를 호출하는 경우에 Sleep Pool에서 빠져나올 수 있으며 빠져나온 스레드는 실행 가능 상태가 되어 Runnable Pool로 들어간다. 대부분 sleep() 메소드는 다른 스레드에게 실행 기회를 양보하기 위한 목적으로 쓰인다.

    join() 메소드
    join() 메소드는 다른 스레드와 협동 작업을 요구할 때 사용할 수 있는 메소드다. 이 메소드는 "합류하기를 기다린다." 라고 해석할 수 있다. 스레드에 대하여 join() 메소드가 호출되면 현재 실행중인 스레드는 Join Pool로 들어가며 대기 상태가 된다. join() 메시지를 전달받은 스레드가 수행을 시작하여 모든 수행이 끝나고 종료 상태가 되면 그때서야 Join Pool에서 대기 중이었던 스레드가 풀려 나와서 실행 가능 상태가 되어 Runnable Pool로 들어간다.
    인자로는 sleep() 메소드와 같이 밀리 세컨드 단위로 표현된 시간을 줄 수 있으며 이 시간이 지날 때 까지 스레드가 종료하지 못할 경우 대기 중이던 스레드가 다시 실행 가능 상태로 전환된다. 물론 인터럽트를 통하여 사용자가 강제로 대기 상태를 풀게 만들 수도 있다. Join() 매소드는 2개 이상의 스레드가 존재할 때 서로 간의 작업이 시간에 따라 영향을 받거나 선후관계가 존재할 때 유용하게 사용될 수 있다.

    synchronized 메소드 호출
    여러 개의 스레드가 하나의 공유 자원 객체를 접근할 때 공유 자원 객체에 존재하고 있는 필드를 보호하기 위하여 synchronized 키워드를 메소드에 지정할 수 있다. 이 키워드가 적용된 메소드를 호출하는 경우에는 공유 객체에 존재하는 모니터링 락을 메소드가 호출한 스레드가 획득하고 객체를 잠가버린다. 이렇게 되면 다른 스레드가 객체의 synchronized 메소드를 호출한 경우, 메소드를 실행하지 못하고 모니터링 락이 반환되어 객체의 잠금 상태가 풀릴 때까지 대기해야 한다. 이런 상태를 synchronized blocking이라고 한다.

    wait(), notify(), noifyAll() 메소드
    스레드가 여러 개 존재하면서 서로 간의 작업을 순차적으로 또는 일정한 순서에 따라 진행하고 싶다면 wait(), notify(), notifyAll() 메소드를 사용하면 된다. wait() 메소드는 스레드를 기다리게 만드는 메소드이다. 중요한 것은 이 메소드가 소속된 클래스는 Object 클래스라는 사실이다. 앞의 다른 메소드들이 Thread 클래스에 소속되어 있는 것과는 달리 wait(), notify(), notifyAll() 메소드는 Object 클래스에 소속되어 있고 synchronized 키워드와 밀접한 관련을 맺고 있다. 즉, 공유 객체에 있는 모니터링 락의 획득, 반환 메커니즘과도 연결되어 있다는 뜻이다.
    Thread 클래스에는 wait(), notify()와 비슷한 역할을 수행하는 suspend(), resume() 라는 메소드가 존재한다. 그러나 이 두 메소드는 deprecated 메소드이며 더 이상 사용하지 않는다. 객체의 모니터링 락을 반환하는 wait() 메소드와는 달리 suspend() 메소드는 모니터링 락을 가지고 잇는 상태에서 스레드가 대기상태로 바뀌기 때문에 교착 상태를 유발할 가능성이 있으므로 사용시 주의해야 한다.
     
    모든 객체는 Wait Pool을 가지고 있다. 이곳에는 객체의 wait() 메소드를 실행하고 동작을 멈춘 스레드들이 저장된더. 여기에 저장된 스레드는 시간이 경과하면 스스로 빠져 나와 실행 가능 상태로 갈 수 이씨만, 대부분 notify(), noifyAll(), 또는 인터럽트에 의해 빠져나온다. wait() 메소드가 호출되면 현재 실행 중인 스레드는 wait() 메시지를 전달받는 객체의 Wait Pool로 들어가며 대기 상태가 된다. Wait Pool에 여러 개의 스레드가 존재할 경우 notify() 메소드가 실행되면 그중 하나의 스레드만이 빠져나와 실행 가능 상태가 되며 Runnable Pool로 들어간다. 어떤 스레드가 빠져나올지는 JVM이 구현한 알고리즘에 따라 달라지지만, 대부분 선입 선출형 큐 구조를 따르는 경우가 많다. 모든 스레드를 빠져나오도록 만들고 싶다면 notifyAll() 메소드를 호출하면 된다.

     
Designed by Tistory.