[Java/자바] 6. 멀티 Thread 프로그래밍

멀티쓰레드 Multithread

멀티태스킹 Multitasking

  • 하나의 컴퓨터로 동시에 여러 가지 일을 수행하는 것
  • 우리가 사용하는 대부분의 OS는 멀티 프로세스를 통해서 멀티태스킹을 지원
  • 하나의 CPU를 가지고 있는 시스템에서 실행시간을 나눠 각 프로세스들이 CPU를 점유

Single Thread Test

public class SingleThreadTest{
    public static void main(String[] args){
        for(int i = 0; i < 10; i++){
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + " : " + i);
        }
    }
}
  • Single Thread Program은 프로그램이 종료될 때까지 하나의 Thread만 수행되는 프로그램

Thread 구조

Thread 구조

Thread 클래스

java.lang 패키지에는 Thread 기능을 구현하는데 이용되는 Thread라는 클래스가 제공된다.

  • 주요 메소드

    • static void sleep(long msec) throws InterruptedException: msec에 지정된 millisecond 동안 Thread를 대기한다.
    • void start(): Thread를 시작하게 한다.
    • void join() throws InterruptedException: Thread가 끝날 때까지 대기한다.
    • void run(): Thread 기능을 실행
    • static void yield(): 현재 실행 중인 Thread를 잠시 멈추어 다른 Thread가 실행될 여지를 준다.

상속

  • Thread 클래스를 활용해서 Thread 프로그램을 작성하는 절차

    1. 특정 기능을 수행하는 Thread 클래스 작성

      class MyThread extends Thread{
          //Thread 변수 선언
          //run()메소드 Overriding
          public void run(){
              //Thread 기능 구현
          }
      }
    2. Thread 클래스로부터 객체를 생성하고 생성된 Thread 객체를 실행한다.

      //Thread 클래스에 대한 객체 생성
      MyThread thread = new MyThread();
      //Thread 실행
      thread.start();
    3. Thread 객체 특징

      • start()메소드를 호출할 때 동작
      • Overriding한 run()메소드가 실행된다.
      • Thread의 실행과 무관하게 main()메소드는 종료(∵ 쓰레드를 기동하는 것으로 역할을 다했음)
      • Thread 객체가 각각 자신이 수행할 작업들을 독립적으로 수행한다.
  • Runnable 인터페이스를 활용해서 Thread 프로그램을 작성하는 절차

    Runnable 인터페이스의 경우 Thread의 수행 코드인 run()메소드를 가지고 있어서 내부적으로는 Thread 객체를 생성해서 수행하도록 되어있다.

    1. 필요한 Thread 기능을 수행하는 클래스 작성

      class MyThread implements Runnable{
          //run()메소드 구현
          public void run(){
              //Thread 기능 작성
          }
      }
    2. Thread 클래스로부터 객체를 생성하고 생성된 Thread 객체를 실행

      //MyThread 객체를 생성한다.
      MyThread runnable = new MyThread();
      
      //위의 생성된 객체를 인자로 Thread 클래스를 생성한다.
      Thread thread = new Thread(runnable);
      
      //Thread 실행
      thread.start()

Thread 프로그래밍

Thread 상태도

Thread 상태도

  • 실행 가능 상태(Runnable Pool)에 있는 쓰레드들 중 하나을 골라 실행 상태로 옮기는 것은 스케줄러에 의해 이뤄진다.

스케줄러는 JVM 안에서 수행되는 특별한 Thread라고 생각할 수 있다. 스케줄러는 실행 상태에 있는 Thread가 CPU를 반납할 경우 여러 상황을 고려하여 다음에 수행될 Thread 후보를 선택한다.

Thread의 제어 메소드

3상태 흐름도

  • 수행 불가능한 상태로 이동시키는 Thread 클래스의 메소드

    • sleep()

      • 실행 중인 쓰레드를 sleeping 상태로 보낸다.
      • 시간이 경과되면 sleeping 상태에서 ready 상태로 전이
      • 다른 Thread에게 실행 기회를 양보하기 위해 사용된다.
    • wait()(notify(), notifyAll())

      • Thread가 여러 개 존재하면서 순차적 또는 일정 순서에 따라 작업하고 싶을 때 사용하는 메소드
      • 다른 메소드들과 달리 Thread 클래스에 소속되지 않고 Object 클래스에 소속되어 있다.
      • Synchronized 예약어와 밀접한 관련을 맺고 있다.
      • 실행 중인 쓰레드가 wait()에 의해 waiting 상태가 되었다가 notify()를 받으면 기다리던 쓰레드 중에 하나만 ready 상태로 전이된다.
      • notify()를 받을 쓰레드가 선택되는 것은 알고리즘에 따라 다르지만 대부분 선입선출 큐 구조를 따른다.
      • 모든 Thread를 빠져나오도록 하고 싶다면 nofifyAll() 메소드 호출
    • join()

      • 협동 작업을 요구할 때 사용할 수 있는 메소드
      • 두 개 이상의 Thread가 존재할 때, 서로 간의 작업이 시간에 따라 영향을 받거나 선후관계가 존재할 때 유용하게 사용할 수 있다.
      • 실행 중인 쓰레드를 joining 상태로 전이 시킨다.
      • join()이 전달된 쓰레드가 종료되면 ready 상태로 전이된다.
    • yield()

      • 한 쓰레드가 수행 상태를 너무 오랫동안 점유하지 않고 다른 쓰레드에게도 기회를 주기 위해서 사용되는 메소드
      • ready 상태의 쓰레드가 yield()에 의해 수행상태로 전이된다.
      • 동일한 우선순위를 갖는 다른 Thread에게 수행 권한을 양보할 수 있게 해준다.
  • IO를 수행하고 있는 경우는 이미 수행되고 있는 객체에 대한 접근이 blocking될 수 있다.
  • run()

    • run()메소드의 내용을 모두 수행하면 쓰레드는 자동으로 종료하고, 쓰레드 수행 시 할당되었던 자원은 모두 해제된다.
    • run()메소드를 모두 수행하기 전에 Thread를 종료할 수 있는 방법은?

      • ❌ stop()메소드를 이용하는 방법: 잘못된 동작을 유발할 가능성이 있어 deprecated 메소드로 분류됨
      • boolean형 플래그를 이용해서 쓰레드를 종료시키기

Thread 스케줄링

  • 우선순위(Priority)에 따른 방식 or Round-Robin(Time Slicing)을 적용하는 방식
  • 자바에서 생성된 모든 쓰레드는 기본적으로 5 우선순위를 가진다.
  • 쓰레드에 할당할 수 있는 우선순위는 1에서 10까지며 1이 가장 낮은 우선순위다.
  • Thread 클래스 멤버

    • final static int MIN_PRIORITY: 가장 작은 우선순위 값, 1
    • final static int NORM_PRIORITY: 중간 값의 우선순위 값, 5
    • final static int MAX_PRIORITY: 가장 큰 우선순위 값, 10
    • final int getPriority(): 우선순위 return
    • final void setPriority(int p): 쓰레드의 우선순위를 p로 설정

동기화 Synchronization

작성하는 응용 프로그램들은 많은 경우에 다수 개의 Thread가 어떤 연관관계를 가지고 동작하게 된다.

  • 임계 영역

    • 하나의 쓰레드가 수행되고 있는 동안 다른 Thread에 의해 접근되지 못하게 해야한다.
    • 파일에 대한 처리를 하는 부분은 한 순간에 하나의 Thread만 사용할 수 있는 영역이 되어야 한다.

synchronized 예약어

  • synchronized 예약어를 통해 동기화 처리가 가능하다.
  • 두 개 이상의 Thread가 하나의 자원을 공유하면서 작업을 진행할 때 자원을 보호하기 위해 사용
  • 메소드 앞에 붙여서 메소드 자체를 동기화 한다.
  • 여러 Thread에 의해 특정 객체의 메소드들이 동시에 호출되는 것에 대해 lock을 설정하는 기능을 가지고 있다.

Thread 사이의 통신

두 개 이상의 Thread가 서로 협력하며 공유 자원을 사용하도록 하는 것

  • wait()
  • nofity()
  • notifyAll(): nofity()에 의해서는 waiting pool에서 한 가지의 쓰레드만 나올 수 있는데 이때 선택되는 기준은 JVM에 따라 다르다. 차라리 모든 쓰레드들을 꺼내고 다시 같은 조건에서 시작하는 것이 더 낫다. 따라서 대부분은 wait()와 notufyAll()을 함께 사용하는 편이다.

Hi! I'm @Yeseul Lee
Passionate for what I love

GitHubLinkedIn