728x90
  • 프로세스란 "실행 중인 프로그램"이다.
    프로그램을 실행하면 OS로부터 실행에 필요한 리소스를 할당받아 프로세스가 된다.
  • 프로세스는 프로그램의 수행에 필요한 데이터와 메모리 등의 자원 그리고 쓰레드로 구성되어 있다.
    그래서 모든 프로세스에는 최소한 하나 이상의 쓰레드가 존재하며, 둘 이상의 쓰레드를 가진 프로세스를 "멀티쓰레드 프로세스"라고 한다.
  • 멀티쓰레딩의 장점은 아래와 같다.
    • CPU의 사용률을 향상시킨다.
    • 자원을 보다 효율적으로 사용할 수 있다.
    • 사용자에 대한 응답성이 향상된다.
    • 작업이 분리되어 코드가 간결해진다.
  • 멀티쓰레드 프로세스는 하나의 프로세스 내에서 여러 쓰레드가 자원을 공유하면서 작업하기 때문에 동기화, 교착 상태와 같은 문제들을 고려해서 신중히 프로그래밍해야 한다.
  • JAVA에서 쓰레드를 구현하는 방법은 "Thread 클래스를 상속받는 방법"과 "Runnable 인터페이스를 구현하는 방법"이 있다.
    Tread클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에, Runnable 인터페이스를 구현하는 방법이 일반적이다.
  • 쓰레드를 생성했다고 자동 실행되는 것은 아니다. start() 메서드를 호출해야 쓰레드가 실행된다.
    이때 실행대기 중인 쓰레드가 없으면 바로 실행되지만, 대기 중인 쓰레드가 있다면 실행대기 상태로 있다가 자신의 차례가 되었을 때 실행된다.
  • 한 번 실행이 종료된 쓰레드는 다시 실행될 수 없다. 즉, 하나의 쓰레드에 대해 start()가 한 번만 호출될 수 있다.
    쓰레드가 작업을 한번 더 수행해야 할 경우 새로운 쓰레드를 생성한 후 start()를 호출해야 한다.
  • 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.
  • 두 개의 작업을 싱글쓰레드로 처리하는 경우와 멀티쓰레드로 처리하는 경우, 멀티 쓰레트가 작업시간이 조금 더 많이 걸리는데 이는 쓰레드간의 작업 전환(context switching)때문이다.
    그래서 싱글 코어에서 단순히 CPU만을 사용하는 계산작업이라면 오히려 싱글쓰레드로 프로그래밍하는 것이 더 효율적이다.
  • 두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 멀티쓰레드 프로세스가 더 효율적이다.
    예를 들어 사용자로부터 데이터를 입력받는 작업, 네트워크로 파일을 주고받는 작업, 프린터로 파일을 출력하는 작업과 같이 외부기기와의 입출력을 필요로 하는 경우가 있다.
  • 사용자가 데이터를 입력하는 작업이 있을 때, 싱글쓰레드 프로세스의 경우 사용자가 입력을 마치기 전까지 다른 작업은 진행되지 않고 해당 작업이 끝나는 것만을 기다리고 있다. 하지만 멀티쓰레드 프로세스의 경우에는 사용자가 입력을 하는 동안 다른 작업이 진행될 수 있다. 때문에 효율적인 CPU 사용이 가능하며 작업 또한 더 빨리 끝낼 수 있다.
  • 쓰레드는 우선순위라는 속성을 가지고 있는데, 해당 값에 따라 쓰레드가 얻는 실행시간이 달라진다.
  • 우선순위의 범위는 1~10이며 숫자가 높을수록 우선순위가 높다.
  • 쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속받는다. main메서드를 수행하는 쓰레드의 우선순위가 5라면 main내에서 생성한 쓰레드의 우선순위는 자동적으로 5가 된다.
  • 쓰레드 그룹은 서로 관련된 쓰레드를 다루기 위한 것으로, 쓰레드를 그룹으로 묶어서 관리할 수 있다.
    또한 쓰레드 그룹 안에 다른 쓰레드 그룹을 포함시킬 수 있다.
  • 모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야 하기 때문에 그룹을 지정하지 않고 생성한 쓰레드는 기본적으로 자신을 생성한 쓰레드와 같은 그룹에 속하게 된다.
  • 데몬 쓰레드는 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드이다.
    데몬쓰레드의 예로는 가비지 컬렉터, 워드프로세서의 자동저장, 화면자동 갱신 등이 있다.
  • 쓰레드는 생성된 후부터 종료될 때까지 여러 상태를 가질 수 있다.
    • NEW: 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태
    • RUNNABLE: 실행 중 또는 실행 가능한 상태
    • BLOCKED: 동기화블록에 의해서 일시정지된 상태
    • WAITING : 쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은 일시정지 상태
      TIMED_WAITING: 일시정지 시간이 지정된 경우
    • TERMINATED: 쓰레드의 작업이 종료된 상태
  • 쓰레드 프로그래밍의 난이도는 동기화와 스케줄링에 때문에 어려운 편이다.
    효율적인 멀티쓰레드 프로그램을 만들기 위해서는 보다 정교한 스케줄링을 통해 프로세스에게 주어진 자원과 시간을 여러 쓰레드가 낭비 없이 잘 사용하도록 프로그래밍해야 한다.
  • 멀티쓰레드 프로세스에서 한 쓰레드가 특정 작업을 끝마치기 전까지 다른 쓰레드에 의해 방해받지 않도록 하는 것이 필요하다. 그래서 도입된 개념이 "임계영역(critical section)"과 "잠금(lock)"이다.
  • 공유 데이터를 사용하는 코드 영역을 임계영역으로 지정하고 공유 데이터(객체)가 가지고 있는 lock을 획득한 단 하나의 쓰레드만 이 영역 내의 코드를 수행할 수 있게 한다.
    그리고 해당 쓰레드가 임계 영역 내의 모든 코드를 수행하고 벗어나서 lock을 반납해야만 다른 쓰레드가 반납된 lock을 획득하여 임계 영역의 코드를 수행할 수 있게 된다.
    이처럼 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것을 "쓰레드의 동기화"라고 한다.
  • synchronized 키워드를 이용해서 간단하게 동기화를 할 수 있다.
    • 메서드 앞에 키워드를 붙일 경우 메서드 전체가 임계영역으로 설정된다.
      쓰레드는 메서드가 호출된 시점부터 해당 메서드가 포함된 객체의 lock을 얻어 작업을 수행하다가 메서드가 종료되면 lock을 반환한다.
    • 메서드 내의 코드 일부를 블록({})으로 감싸고 블록 앞에 "synchronized(참조변수)"를 붙일 경우 해당 블록이 임계 영역이 된다.
      블록의 안으로 들어가면서부터 쓰레드는 참조변수로 지정된 객체의 lock을 얻게 되고, 블록을 벗어나면서 반납한다.
  • synchronized를 통해 동기화하여 공유 데이터를 보호할 수 있지만, 특정 쓰레드가 객체의 lock을 가진 상태로 오랜 시간을 보낼 수도 있다. 그런 상황을 개선하기 위해 고안된 것이 wait(), notify()이다.
    임계영역 내에서 작업을 수행하다가 작업을 더 이상 진행할 수 있는 상황이 아닌 경우에 wait()를 호출하여 쓰레드의 lock을 반납하고 대기한다. 그러다가 작업 가능한 상황이 되었을 때 notify()를 호출하여 작업을 중단했던 쓰레드가 다시 lock을 얻어 작업을 수행할 수 있다.
    이때 wait()에 의해 lock을 잃었다가 다시 lock을 얻어 임계영역에 들어오는 것을 "재진입(reentrance)"라고 한다.

+ Recent posts