[Java] 스레드(Thread) 개념 정리 - sleep(), wait(), join()

2022. 2. 27. 14:55Back-end/Java

스레드의 개념


프로세스(process)란?

프로세스(process)란 단순히 실행 중인 프로그램(program)이라고 할 수 있다.
즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것을 말한다.
이러한 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성된다.

스레드(thread)란?

스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미한다.
모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다.
또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다.

 

멀티 스레딩(multi-threading)

  • 하나의 프로세스 내에서 둘 이상의 thread가 동시에 작업을 수행하는 것을 의미한다
  • thread는 각각 자신만의 작업 공간을 가진다(context)
  • 각 thread 사이에서 공유하는 자원이 있을 수 있다 (자바에서는 static instance)
  • 여러 thread가 자원을 공유하여 작업이 수행되는 경우 서로 자원을 차지하려는 race condition이 발생할 수 있다
  • 여러 thread가 공유하는 자원중 경쟁이 발생하는 부분을 critical section이라고 한다
  • critical section에 대해 동기적(순차적)인 구현하지 않으면 오류가 발생할 수 있다

 

critical section 발생

자바에서 Thread 만들기

Thread 클래스를 상속해서 만들기

두 개의 스레드를 실행
Console

Runnable 인터페이스를 구현해서 만들기

Runnable interface

자바는 다중 상속을 허용하지 않는다.
그래서 이미 다른 클래스를 상속한 경우 Runnable interface를 구현해서 thread를 만든다.

스레드 상태 제어(thread state control)

sleep() : 지정된 시간(밀리초) 동안 현재 thread를 일시 중단한다
try{
    Thread.sleep(1000);
}catch(InterruptedException e){
    e.printStackTrace();
}

 

  • wait() : 갖고 있던 고유 락을 해제하고 thread를 잠들게 한다.
  • notify() : 잠들어 있던 thread 중 임의로 하나를 골라 깨운다.
  • wait와 notify는 동기화된 블록 안에서 사용해야 한다. wait를 만나게 되면 해당 thread는 해당 객체의 모니터링 락에 대한 권한을 가지고 있다면 모니터링 락의 권한을 놓고 대기한다.
Thread를 상속받은 ThreadB 클래스를 사용하여 wait()을 호출하는 예제
class ThreadB extends Thread {
	// 해당 thread가 실행되면 자기 자신의 모니터링 락을 획득
	// 5번 반복하면서 1초씩 쉬면서 total에 값을 누적
	// 그후에 notify()메소드를 호출하여 wiat하고 있는 thread를 깨움
	int total;

	@Override
	public void run() {
		synchronized (this) {
			for (int i = 0; i < 5; i++) {
				System.out.println("ThreadB : " + i);
				total += i;
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			notify();
		}
	} // run
}

public class ThreadWait {

	public static void main(String[] args) {
		// 해당 thread가 실행되면, 해당 thread는 run메서드 안에서 자신의 모니터링 락을 획득
		ThreadB b = new ThreadB();
		b.start();

		// b에 대하여 동기화 블럭을 설정
		// 만약 메인 thread가 아래의 블록을 위의 Thread보다 먼저 실행되었다면 wait를 하게 되면서 모니터링 락을 놓고 대기
		synchronized (b) {
			try {
				// b.wait()메소드를 호출.
				// 메인 thread는 정지
				// ThreadB가 5번 값을 더한 후 notify를 호출하게 되면 wait에서 깨어남
				System.out.println("b 종료까지 대기");
				b.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			// 깨어난 후 결과를 출력
			System.out.println("total : " + b.total);
		}
	}
}​
[Console]
b 종료까지 대기
ThreadB : 0
ThreadB : 1
ThreadB : 2
ThreadB : 3
ThreadB : 4
total : 10

 

  • join() : 다른 thread가 멈출 때까지 기다리게 한다

  • 동시에 두 개 이상의 thread가 실행될 때 다른 thread의 결과를 참조하여 실행해야 하는 경우 사용한다
  • join() 함수를 호출한 thread는 non-runnable 상태가 되고 다른 thread의 수행이 끝나면 runnable 상태로 돌아온다
스레드를 실행하고 해당 스레드가 종료될 때까지 기다리고 내용을 출력하는 예제
class MyThread2 extends Thread {
	
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("MyThread2 : " + i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	} // run
}

public class ThreadJoin {
	
	public static void main(String[] args) {
		
		MyThread2 thread = new MyThread2();
		// Thread 시작
		thread.start();
		System.out.println("thread 종료까지 대기");
		try {
			// 해당 스레드가 멈출 때까지 대기
			thread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("thread 종료");
	}
	
}
[Console]
thread 종료까지 대기
MyThread2 : 0
MyThread2 : 1
MyThread2 : 2
MyThread2 : 3
MyThread2 : 4
thread 종료

 

  • interrupt() : thread가 sleep(), wait(), join() 함수에 의해 non-runnable 상태일 때 interrupt()를 호출하면 다시 runnable 상태가 된다

 

반응형