Java08_멀티스레드

4 minute read



  • 이 글은 이것이 자바다 - 신용권 지음 책을 공부하며 작성한 글입니다.



1. 멀티스레드 기본 용어

1-1. 프로세스와 스레드

  • 프로세스 : 실행 중인 하나의 애플리케이션

    멀티태스킹 : 두 가지 이상의 작업을 동시에 처리

  • 멀티프로세스
    • 애플리케이션 단위의 멀티 태스킹
    • 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향 X
  • 멀티스레드
    • 하나의 프로세스가 두 가지 이상의 작업을 처리
    • 애플리케이션 내에서의 멀티태스킹
    • 하나의 스레드에서 예외 발생시 다른 프로세스 자체가 종료되므로 예외처리 필수

1-2. 메인스레드

  • 싱글 스레드 애플리케이션에서는 메인스레드가 종료하면 프로세스도 종료된다.
  • 멀티 스레드 애플리케이션에서는 실행 중인 스레드가 하나라도 있다면, 프로세스는 종료되지 않는다.
  • 메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 계속 실행중이라면 프로세스는 종료되지 않음.




2. 작업 스레드 생성과 실행

  1. java.lang.Thread클래스를 직접 객체화하여 생성
  2. Thread를 상속하여 하위 클래스를 만들어 생성

2-1. Thread클래스로부터 직접 생성

  • Runnable을 매개값으로 갖는 생성자를 호출해야한다.
  • run( ) 메소드를 재정의한다.
  • start( )메소드를 호출하면 Runnable의 run( )메소드를 실행하면서 자신의 작업 처리
Thread thread = new Thread(Runnable target);

public class BeepTask implements Runnable{ }
  • 실행순서
    1. 메인스레드
    2. (main) 스레드객체 생성 (class Task implements Runnable)
    3. (main) start( ) 메소드 호출
    4. 스레드 객체에서 run() 메소드 실행
    5. 작업 스레드 실행

2-2. Thread 하위 클래스로부터 생성

  • Thread클래스를 상속하여 run 메소드를 재정의(overriding)해서 스레드가 실행할 코드를 작성
public class WorkerThread extends Thread {

  @Override
  public void run(){
    //스레드가 실행할 코드
  }
  
}
  • 실행순서
    1. 메인스레드
    2. (main) 스레드객체 생성
    3. (main) start( )메소드 호출
    4. 스레드 객체의 run() 메소드 실행

2-3. 스레드 이름

  • 스레드 객체의 참조
Thread thread = Thread.currentThread();


  • 스레드 이름 설정
thread.setName("스레드 이름);


  • 스레드 이름 가져오기
thread.getName();




3. 스레드 우선순위

  • 동시성
    • 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질
    • 싱글코어 CPU 멀티스레드 작업은 병렬적으로 실행되는 것 처럼 보이지만, 번갈아가며 실행하는 동시성 작업
  • 스레드 스케쥴링
    • 스레드의 개수가 코어의 수보다 많을 경우, 스레드를 어떤 순서에 의해 동시성으로 실행할 것인가를 결정
    • 아주 짧은 시간에 번갈아가며 run() 메소드를 조금씩 실행
    • 우선순위방식 / 순환할당방식
      • 우선순위방식 : 우선순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케쥴링 ( 개발자가 제어 가능 )
      • 순환할당방식 : 시간 할당량을 정해서 하나의 스레드를 정해진 시간만큼 실행하고 다시 다른 스레드를 실행. ( 개발자 제어 불가 )




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

  • 멈티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야하는 경우가 있다.
  • 스레드 A를 사용하던 객체가 스레드 B에 의해 상태가 변경될 수 있으므로 다른 결과를 산출할 수도 있다.

4-1. 동기화

  • 임계영역 : 단 하나의 스레드만을 실행할 수 있는 코드 영역
  • 자바는 임계영역을 지정하기 위해 synbchronized메소드와 동기화 블록을 제공한다.
  • 스레드는 동기화 메소드를 실행하는 즉시 객체에 잠금이 일어나고, 실행 종료시 잠금이 풀림




5. 스레드 상태

  • 실행대기 상태
    • 스케쥴링이 되지 않아서 실행을 기다리고 있는 상태
    • 종류
      • RUNNABLE
  • 실행상태
    • 실행대기 상태의 스레드 중 스레드 스케쥴링으로 선택된 스레드가 CPU를 점유하고 run( )메소드를 실행하는 상태
    • 실행상태의 스레드는 run( )메소드를 모두 실행하기 전에 스레드 스케줄링에 의해 다시 실행 대기상태로 돌아갈 수 있음
    • 실행 대기상태에 있는 다른 스레드가 선택되어 실행상태가 된다.
    • 실행과 실행대기 상태를 번갈아가며 run( )메소드를 조금씩 실행
  • 종료상태
    • run( )메소드 종료시 더 이상 실행할 코드가 없으므로, 실행이 멈추게 된다.
    • 종류
      • TERMINATED
  • 일시정지상태
    • 실행상태에서 대기가 아닌 일시정지 상태가 되기도 함.
    • 종류
      • WAITING, TIMED_WAITING, BLOCKED




6. 스레드 상태 제어

메소드 설명
interrupt() Thread 클래스
일시정지 상태의 스레드에서 InterruptedException 예외를 발생시켜 예외처리코드(catch)에서 실행대기 상태로 가거나 종료 상태로 갈 수 있도록 한다.
notify()
notifyAll()
Object 클래스
동기화 블록 내에서 wait()메소드에 의해 일시정지 상태에 있는 스레드를 실행 대기 상태로 만든다.
resume() Thread 클래스
suspend()메소드에 의해 일시정지 상태의 스레드를 실행대기 상태로 만든다.
- Deprecated(대신 notify(), notifyAll() 사용)
sleep(long mills)
sleep(long mills, int nanos)
Thread 클래스
주어진 시간동안 스레드를 일시정지 상태로 만든다. 주어진 시간이 지나면 자동적으로 실행대기 상태가 된다.
join()
join(long mills)
join(long mills, int nanos)
Thread 클래스
join()메소드를 호출한 스레드는 일시정지 상태가 된다. 실행대기 상태로 가려면, join()메소드를 멤버로 가지는 스레드가 종료되거나 매개값으로 주어진 시간이 지나야 한다.
wait()
wait(long mills)
wait(long mills, int nanos)
Object 클래스
동기화(synchronized)블록 내에서 스레드를 일시정지 상태로 만든다. 매개값으로 주어진 시간이 지나면 자동적으로 실행대기 상태가 된다. 시간이 주어지지 않으면 notifiy(), notifyAll()메소드에 의해 실행대기 상태로 갈 수 있다.
suspend() Thread 클래스
스레드를 일시정지 상태로 만든다. resume() 메소드를 호출하면 다시 실행 대기 상태가 된다. - Deprecated (대신 wait()사용)
yield() Thread 클래스
실행중에 우선순위가 동일한 다른 스레드에게 실행을 양보하고 실행 대기 상태가 된다.
stop() Thread 클래스
스레드를 즉시 종료 시킨다. - Deprecated




7. 데몬스레드

  • 주 스레드의 작업을 돕는 보조적인 역할을 하는 스레드
  • 주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료 ( 주 스레드가 종료되면 존재 의미가 없음 )
  • 워드프로세스, 미디어플레이어, JVM이 종료되면 같이 종료.
  • 스레드를 데몬으로 만들기 위해서는 주 스레드가 데몬이 될 스레드의 setDaemon(true)를 호출해주면 된다.
public static void main(String[] args){
  AutoSavThread thread = new AutoSaveThread();
  thread.setDaemon(true);
  thread.start();
}




8. 스레드 그룹

  • 관련된 스레드를 묶어서 관리
  • JVM이 실행되면 system스레드 그룹을 만들고 JVM운영에 필요한 스레드를 생성하여 system스레드 그룹에 포함시킨다.
  • system의 하위 스레드 그룹으로 main을 만들고 메인 스레드를 main스레드 그룹에 포함시킨다.
  • 스레드는 반드시 하나의 스레드 그룹에 포함된다.
  • 명시적으로 스레드 그룹에 포함시키지 않으면 기본적으로 자신을 생성한 스레드와 같은 스레드 그룹에 속한다.
  • 작업스레드 대부분은 main스레드가 생성하므로 기본적으로 main스레드 그룹에 속하게 됨.

8-1. 스레드 그룹 이름 얻기

ThreadGroup group = Thread.currentThread().getThreadGroup();
// 스레드그룹 이름얻기
String groupName = group.getName();

// 모든 정보 얻기
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();

8-2. 스레드 그룹 생성하기

ThreadGroup tg = new ThreadGroup(String name);
ThreadGroup tg = new ThreadGroup(ThreadGroup parent, String name);




9. 스레드풀

  • 병렬 작업 처리가 많아지면 스레드 개수가 증가되고, 스레드 생성과 스케쥴링으로 인해 CPU가 바빠져 메모리 사용량이 늘어난다.
  • 이러한 병렬 작업의 폭증으로 인한 스레드의 폭증을 막으려면 ThreadPool을 사용해야 한다.
  • 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리
  • java.util.concurrent 패키지에서 ExecutorService인터페이스와 Executors클래스를 제공