ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Thread 정리
    java 2021. 5. 11. 15:55

    배경

    프로세스가 무엇인지 스레드가 무엇인지에 대한 내용은 여기서 다루지 않는다. 우선 java에서 스레드를 관리하기 위해 제공하는 객체가 무엇인지 확인하고 간단한 api사용법 및 스레드 상태에 대해 정리하도록 하자.

     

     

    스레드 실행

    스레드를 실행하기 위해서는 Thread.start()를 수행해야 한다. start는 내부적으로 run()을 호출하게 되는데 run()을 정의하기 위한 방법으로 Thread객체를 상속하거나 Runnable인터페이스를 구현해 재정의한 run()을 콜백으로 넘겨주는 방법이 있다.

    전형적인 템플릿콜백 패턴이다. 참고로 한 스레드 당 한 번만 start()를 호출할 수 있다.  스레드는 스레드만의 독립적인 메모리 영역을 갖고 있고 고유한 이름이 있다. 따라서 Thread의 static메서드를 이용해 현재 스레드의 정보에 접근하는 것도 가능하다.

     

     

    스레드 그룹

    JVM은 스레드를 그루핑하여 관리한다. 애플리케이션 실행 시 main과 system그룹을 만든다. main은 말그대로 main메서드를 수행하는 스레드를 담고 system에는 GC를 담당하는 스레드를 담는다. 새로 생성한 스레드는 그 스레드를 생성한 스레드의 그룹에 속하게 된다. 

     

    데몬 스레드

    데몬스레드는 시스템을 운영하기 위한 스레드이다. 대표적으로 GC스레드가 있다. 데몬스레드는 무한루프 또는 조건문을 이용해 대기하고 있다가 특정 조건이 만족되면 작업을 수행하고 다시 대기한다. 또한 일반 스레드가 모두 종료되면 자동으로 데몬스레드도 종료된다. 데몬스레드가 생성한 스레드 또한 데몬스레드이다.

     

    스레드 상태 및 메서드

    스레드의 개수는 무수히 많아질 수 있지만 스레드를 실행할 코어의 개수는 제한적이다. 적절한 스케줄링을 통해 원하는 목표를 이뤄야하고, 스레드를 스케줄링한다면 여러가지 상태로 나눌 수 있다.

    상태

    - NEW : 스레드가 생성되고 start()가 호출되기 전

    - RUNNABLE : 실행 중 또는 실행 가능한 상태

    - BLOCKED: lock이 걸린 상태(동기화 자원을 얻길 기다리는 상태)

    - WAITING, TIMED_WAITNG : 스레드 일시정지 상태(인터럽트, sleep, 등등)

    - TERMINATED : 스레드 작업이 종료된 상태

     

    메서드

    - sleep() : static 메서드로 본인을 일정시간 잠재운다

    - join() : 호출한 스레드가 실행권한을 얻게 된다

    - interrupt() : 일시정지상태 스레드를 실행대기 상태로 만든다

    - stop() : 스레드를 중지시키다.

    - suspend() : 스레드를 일시정지 시킨다

    - yield() : 실행시간을 즉시 다음 스레드에 양도함으로써 busy-waiting을 개선한다.

     

    join(), sleep(), yield()를 명확히 구분해보자

    - join() : 스레드A에서 스레드B를 join한다면 스레드A는 스레드B가 실행될 때까지 대기상태로 간다

    - sleep() : 현재 스레드가 특정시간동안 대기상태(스케줄링 대상 x)

    - yield() : 현재 스레드를 대기상태로 돌리고 스케줄링의 대상이 되게 함

     

     

    동기화

    객체 인스턴스 또는 클래스의 static 멤버 변수는 다수의 스레드가 공유하는 메모리 영역이다. 때문에 라운드로빈 기반 멀티스레드 환경에서 반복되는 쓰기 작업을 할 경우 원하는 대로 결과가 안 나올 수 있다. 이러한 문제를 동기화 문제라 하고 java에서는 이런 문제를 해결하기 위한 알고리즘을 제공한다. 프로그래머는 그저 synchronized 키워드만 붙임으로써 손쉽게 동기화 문제를 해결한다.

     

    다만, synchronized 키워드는 프로그램의 성능을 크게 저하시킬 수 있다. 이는 자바의 문제가 아닌 시스템 근본적인 문제이며 최대한 동기화 알고리즘을 사용하지 않도록 프로그램을 작성하는 것이 중요하다.

     

    - wait() : 락을 얻은 스레드는 synchronized블럭을 모두 수행하면 자동으로 락을 반납한다. 이때 블럭 수행 도중 문제가 생겨 락을 반납해야 할 때 wait()을 호출해 락을 반납하고 다시 락을 얻기 원하는 대기상태로 가게 된다.

    - notify() : 락을 사용하는 스레드가 락을 반납했으니 락을 얻으러 오라는 메서드다.

     

    - stravation : 락을 얻기 원하는  스레드가 우선순위에 계속 밀려 오랫동안 락을 얻지 못하는 상태

    - race condition : 여러 스레드가 동시에 락을 얻기 원할 때 적절한 락 배분 알고리즘이 없는 상태

     

     

    fork & join 프레임워크

    하나의 작업을 여러 쓰레드가 나눠 처리할 수 있도록 한다. 

    기본적으로 재귀호출을 이용해 작업을 멀티스레드로 병렬 처리했다. 종결조건처럼 전체 사이즈가 특정값 이하라면 sum()연산을 수행하고 그렇지 않다면 작업을 반으로 나눠 분할한다. 분할한 작업을 하나는 compute()연산을 동일 스레드에서 작업하고 다른 하나는 fork()로 작업큐에 넣은 뒤 join()으로 스레드의 스케줄을 받을 수 있도록 한다.

    (내용이 좀 어려우니 나중에 상세하게 내부 코드까지 뜯어보자)

     

    todo : Lock과 Condition을 이용한 동기화, volatile키워드

     

     

    'java' 카테고리의 다른 글

    Effective Java 모든 객체의 공통 메서드  (0) 2021.05.25
    람다 정리 및 예시  (0) 2021.05.19
    애너테이션 정리  (0) 2021.05.11
    컬렉션 정리  (0) 2021.05.10
    Java Garbage Collector  (0) 2021.05.10

    댓글

Designed by Tistory.