카테고리 없음

[JAVA] 스레드와 멀티태스킹

동누크 2022. 12. 26. 15:53

스레드(Thread)

  • 프로그램 코드를 이동하면서 실행하는 하나의 제어

자바의 멀티태스킹

  • 멀티쓰레딩만 가능
    - 자바에 프로세스 개념은 존재하지 않고, 스레드 개념만 존재
    • 스레드는 실행 단위이자 스케쥴링 단위
  • 하나의 응용 프로그램은 여러 개의 스레드로 구성 가능

자바 스레드

  • JVM(Java Virtual Machine)에 의해 스케쥴되는 실행 단위의 코드 블럭
  • 스레드의 생명 주기는 JVM에 의해 관리
  • 하나의 JVM은 하나의 자바 응용 프로그램만 실행
    - 하나의 응용 프로그램은 하나 이상의 스레드로 구성 가능

스레드를 만드는 2가지 방법

  • java.lang.Thread 클래스를 이용하는 경우
  • java.lang.Runnable 인터페이스를 이용하는 경우

1. Thread 클래스를 이용한 스레드 생성

1) Thread 클래스 상속, 새 클래스 작성
2) run() 메소드 오버라이딩

class TestThread extends Thread {
    ...
    public void run() {
    	// run() 메소드 오버라이딩
        ...
    }
}

3) 스레드 객체 생성

TestThread thread = new TestThread();

4) 스레드 시작 - start() 메소드 호출

thread.start();
  • 주의 사항
    - run() 메소드가 종료되면 스레드는 종료됨
    - 한 번 종료된 스레드는 다시 시작할 수 없음 = 다시 스레드 객체를 생성하고 등록해야 함
    - 스레드에서 다른 스레드를 강제 종료 가능

2. Runnable 인터페이스로 스레드 생성

1) Runnable 인터페이스로 새 클래스 구현
2) 스레드 코드 작성

class TestRunnable implements Runnable {
	...
    public void run() {
    	// run() 메소드 구현
        ...
    }
}

3) 스레드 객체 생성

Thread thread = new Thread(new TestRunnable());

4) 스레드 시작

thread.start();

Thread 클래스 상속과 Runnable 인터페이스 구현의 차이

  • 자바는 다중 상속을 지원하지 않는다. 그렇기 때문에 Thread 클래스를 상속받는 경우, 다른 클래스를 상속받을 수 없다. 그렇기 때문에 Runnable 인터페이스를 구현하는 것이 일반적이다.

스레드 상태 6가지

  • NEW : 스레드가 생성되었지만 아직 실행할 준비가 되지 않음
  • RUNNABLE : 스레드가 JVM에 의해 실행되고 있거나 실행 준비되어 스케쥴링을 기다리는 상태
  • WAITING : 다른 스레드가 notify(), notifyAll()을 불러주기를 기다리고 있는 상태, 보통 스레드 동기화를 위해 사용
  • TIMED_WAITING : 스레드가 sleep(n)을 호출하여 n ms동안 잠을 자는 상태
  • BLOCK : 스레드가 I/O 작업 요청을 하면 JVM이 자동으로 BLOCK 상태로 만듦
  • TERMINATED : 스레드 종료

스레드 생명 주기

스레드 종료와 다른 스레드 강제 종료

  • 스스로 종료하는 경우 : run() 메소드 리턴
  • 다른 스레드에서 강제 종료하는 경우 : interrupt() 메소드 사용
class TestThread extends Thread {
	int n=0;
    public void run() {
    	while(true) {
            n++;
            try {	
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            	return;	// 예외 처리로 종료
            }
        }
    }
}
public class InterruptEx {
    public static void main(Stirng[] args) {
        TestThread thread = new TestThread();
        thread.start();
        thread.interrupt(); // 스레드 강제 종료
    }
}

flag를 이용한 종료

  • 스레드 안의 flag 변수를 이용하여 종료
class TestThread extends Thread {
	int n=0;
    bool flag = false; // false로 초기화
    public void finish() {
    	flag = true;
    }
    public void run() {
    	while(true) {
            n++;
            try {
                Thread.sleep(1000);
                if (flag == true)
                	return;
            } catch (InterruptException e) {
            	return;
            }
        }
    }
}
public FlagEx {
	public static void main(String[] args) {
    	TestThread thread = new TestThread();
        thread.start();        
        tread.finish();	// TestThread 강제 종료
    }
}

스레드 동기화 (Thread Synchronization)

  • 멀티스레드 프로그램 작성 시 주의점
    - 다수의 스레드가 공유 데이터에 동시에 접근하는 경우
    - 공유 데이터의 값에 예상치 못한 결과 발생 가능
  • 이를 해결하기 위해 스레드 동기화 사용
  • 공유 데이터를 접근하는 모든 스레드를 관리
  • 한 스레드가 공유 데이터에 접근하는 경우 작업이 끝날 때까지 다른 스레드들은 대기

synchronized

  • 한 스레드가 독점적으로 실행해야 하는 부분을 표시하는 키워드
  • synchronized 메소드
synchronized void add() {
	int n = getCurrentSum();
    n+=10;
    setCurrentSum(n);
}
  • synchronized 코드 블럭
void execute() {
	...
    synchronized(this) {
    	int n = getCurrentSum();
        n+=10;
        setCurrentSum(n);
    }
    ...
}

wait(), notify(), notifyAll()

  • 동기화 객체 : 2개 이상의 스레드 사이에 동기화 작업에 사용되는 객체
  • 동기화 메소드
    • synchronized 블록 내에서만 사용되어야 함
    • wait()
      • 다른 스레드가 notify()를 불러줄 때까지 대기
    • notify()
      • wait()을 호출하여 대기 중인 스레드를 깨우고 RUNNABLE 상태로 만듦
      • 2개 이상의 스레드가 대기 중이어도 한 개의 스레드만 깨움
    • notifyAll()
      • wait()를 호출하여 대기 중인 모든 스레드를 깨우고 모두 RUNNABLE 상태로 만듦

이미지 출처 : 명품 JAVA 프로그래밍 (황기태, 김효수 저)