ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java 자바] 12. 멀티 스레드 ② 동기화, 스레드 상태와 제어
    Programming/Java 2022. 9. 12. 15:19
    반응형

    12-4. 동기화 메소드와 동기화 블록

    12-4-1. 공유 객체를 사용할 때 주의점

    - 싱글 스레드 프로그램에서는 한 개의 스래드가 객체를 전부 사용하지만,
       멀티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야 하는 경우 발생

    - 이 경우, 다른 스레드에 의해 객체가 상태 변경이 되어 의도와 다른 결과가 산출될 수 있기 때문에 주의해야 함

    12-4-2. 동기화 메소드 및 동기화 블록

    - 임계 영역 (critical section): 멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역

     => 자바에서는 이를 지정하기 위해 동기화 메소드와 동기화 블록을 제공

     => 스레드가 객체 내부의 동기화 메소드 또는 블록에 들어가면 즉시 객체에 잠금을 걸어 다른 스레드가 임계 영역의 코드를 실행하지 못 함

    // 1. 동기화 메소드 만들기 (synchronized 키워드 활용)
    public synchronized void method() {
      임계 영역; // 단 하나의 스레드만 실행
    }
    
    // 2. 동기화 블록 (메소드의 일부 내용만 임계 영역으로 만들 경우)
    public void method() {
      // 여러 스레드가 실행 가능한 영역
      ...
      synchronized(공유객체) {			// 공유객체가 자신이면 this 삽입 가능
        임계 영역 // 단 하나의 스레드만 실행
      }
      // 여러 스레드가 실행 가능한 영역
      ...
    }

    12-5. 스레드 상태

    - 실행 대기: start() 메소드 호출 (스케줄링이 되지 않아 실행을 기다리는 상태)

    - 실행: 스레드 스케줄링으로 선택된 스레드가 CPU를 점유하고 run() 메소드를 실행 

    => 실행 대기와 실행을 번갈아가면서 스레드의 run() 메소드가 조금씩 실행됨

    - 종료: 더 이상 실행할 코드가 없는 상태

    - 일시정지: 스레드가 실행할 수 없는 상태

    // getState() 메소드를 통해 스레드 상태에 대한 열거 상수를 리턴받을 수 있음
    Thread.State state = targetThread.getState();
    상태 열거 함수 설명
    객체 생성 NEW 스레드 객체가 생성, 아직 start() 메소드가 호출되지 않은 상태
    실행 대기 RUNNABLE 실행 상태로 언제든지 갈 수 있는 상태
    일시 정지 WAITING 다른 스레드가 통지할 때까지 기다리는 상태
    TIMED_WAITING 주어진 시간 동안 기다리는 상태
    BLOCKED 사용하고자 하는 객체의 락이 풀릴 때까지 기다리는 상태
    종료 TERMINATED 실행을 마친 상태

    12-6. 스레드 상태 제어

    : 미디어 플레이어에서 동영상을 보다가 일시 정지 시키거나 종료시킬 수도 있는 것처럼, 스레드의 상태를 변경하는 것

    12-6-1. 주어진 시간 동안 일시 정지 (sleep())

    - 실행 중인 스레드를 일정 시간 멈추게 하고 싶을 때 (밀리세컨드 단위로 입력)

    try {
      Thread.sleep(1000);
    } catch(InterruptedException e) {
      // 일시 정지 상태에서 주어진 시간이 되기전에 interrupt() 메소드가 호출되면 실행
    }

    12-6-2. 다른 스레드에게 실행 양보 (yield())

    - 반복문들이 무의미한 반복을 계속할 경우, 다른 스레드에게 실행을 양보하고 자신은 실행 대기 상태로 가게 하는 것

    public void run() {
      while(true) {
        if(work) {
          System.out.println("ThreadA 작업 내용")
        } else {
          Thread.yield();
        }
      }
    }

    12-6-3. 다른 스레드의 종료를 기다림 (join())

    - 다른 스레드가 종료될 때까지 기다렸다가 실행해야 하는 경우 사용 (다른 스레드의 계산 결과값을 활용해야할 때 등)

    try {
      sumThread.join(); 	// sumThread가 종료될 때까지 메인스레드를 일시 정지
    } catch (InterruptedException e) {
    }

    12-6-4. 스레드 간 협업 (wait(), notify(), notifyAll())

    - 경우에 따라 두 개의 스레드를 교대로 번갈아며 실행해야 할 경우, 공유 객체를 활용

    - 공유 객체는 두 스레드가 작업할 내용을 각각 동기화 메소드로 구분해 놓음

     1) 한 스레드가 작업을 완료하면 notify() 메소드를 호출해 일시 정지 상태의 다른 스레드를 실행 대기 상태로 만듦

     2) 자신은 두 번 작업하지 않도록 wait() 메소드를 호출하여 일시 정지 상태로 만듦

         ( wait(long timeout) 이나 wait(long timeout, int nanos)를 활용하면 자동으로 시간이 지나면 실행 대기 상태가 됨)

    // WorkObject.java
    public class WorkObject {
      public synchronized void methodA() {
        System.out.println("ThreadA의 methodA() 작업 실행");
        notify();
        try {
          wait();
        } catch {InterruptedException e) {
        }
      }
      
      public synchronized void methodB() {
        System.out.println("ThreadB의 methodB() 작업 실행");
        notify();
        try {
          wait();
        } catch {InterruptedException e) {
        }
      }
    }
    
    // ThreadA.java
    public class ThreadA extends Thread {
      private WorkObject workobject;
       
      public ThreadA(WorkObject workObject) {
        this.workObject = workObject;
      }
      
      @Override
      public void run() {
        for(int i = 0; i < 10; i++) {
          workObject.methodA();
        }
      }
    }
    
    // ThreadB.java
    public class ThreadB extends Thread {
      private WorkObject workobject;
       
      public ThreadB(WorkObject workObject) {
        this.workObject = workObject;
      }
      
      @Override
      public void run() {
        for(int i = 0; i < 10; i++) {
          workObject.methodB();
        }
      }
    }
    
    // WaitNotifyExample.java
    public class WaitNotifyExample {
      public static void main(String[] args) {
        WorkObject sharedObject = new WorkObject();
        
        ThreadA threadA = new ThreadA(sharedObject);
        ThreadB threadB = new ThreadB(sharedObject);
        
        threadA.start();
        threadB.start();
      }
    }

    12-6-5. 스레드의 안전한 종료 (stop 플래그, interrupt())

    ① stop 플래그를 이용하는 방법

    - run() 메소드가 정상적으로 종료되도록 유도하는 것

    public class XXXThread extends Thread {
      private boolean stop; // 스탑 플래그 필드
      
      public void run() {
        while(!stop) {
          스레드가 반복 실행하는 코드;
        }
        // 스레드가 사용한 자원 정리
      }
    }

    ② interrupt() 메소드를 이용하는 방법

    - 스레드가 일시 정지 상태에 있을 때 InterruptedException 예외를 발생시키는 역할

    // InterruptExample.java
    public class InterruptExample {
      public static void main(String[] args) {
        Thread thread = new PrintThread2();
        thread.start();
        
        try { Thread.sleep(1000);} catch (InterruptedException e) {}
        thread.interrupt();
      }
    }
    
    // PrintThread.java
    public class PrintThread extends Thread {
      public void run() {
        try {
          while(true) {
            System.out.println("실행 중");
            Thread.sleep(1);
          }
        } catch(InterruptedException e) {
        }
       
        System.out.println("자원 정리");
        System.out.println("실행 종료");
      }
    }

    - interrupt() 호출 여부 확인하는 메소드

    boolean status = Thread.interrupted();
    boolean status = objThread.isInterrupted();

     

    반응형

    댓글

Designed by Tistory.