[CS/운영체제] 멀티스레드와 동시성 - 4부

황제연·2025년 6월 27일
0

CS학습

목록 보기
118/193
post-thumbnail

join

이번에는 join() 메소드를 통해 WAITING 상태에 대해 알아보겠습니다

WAITING(대기 상태)

스레드가 다른 스레드의 특정 작업이 완료되기를 무기한 기다리는 상태입니다

public class testWaiting{

	public static void main(String[] args){
		Thread thread1 = new Thread(new Job(), "thread1");
		Thread thread2 = new Thread(new Job(), "thread2");

		thread1.start();
		thread2.start();
	}
	public class Job implements Runnable {
		@Override
		public void run(){
			sleep(5000);
		}
	}

}

위 코드에서는 thread1과 thread2는 각각 특정 작업을 수행합니다
작업은 각각 실행되지만 실행 순서는 보장되지 않으며, main 스레드의 경우
각 스레드를 실행시킨 뒤, 먼저 종료되기 때문에 main에서 특정 작업에 대한 스레드들의 결과를
확인할 수는 없습니다

public class TestWaiting{

	public static void main(String[] args){
		TaskSum task1 = new TaskSum(1, 100);
		Tastsum task2 = new TaskSum(101, 200);
		Thread thread1 = new Thread(task1, "thread1");
		Thread thread2 = new Thread(task2, "thread2");

		thread1.start();
		thread2.start();
		int sum = task1.res + task2.res;
		System.out.print(sum);
	}
	static class TaskSum implements Runnable {
		int start;
		int end;
		int res = 0;

		public TaskSum(int start, int end){
			this.start = start;
			this.end = end;
		}

		@Override
		public void run(){
			sleep(5000);
			int sum = 0;
			for(int i=start; i<=end; i++){
				sum += i;
			}
			res = sum;
			System.out.println("finish!!! " + sum);
		}
	}

}

이번에는 각 스레드가 작업을 할 수 있도록,
1번 스레드는 1~100, 2번 스레드는 101 ~ 200을 더하는 작업을 추가했습니다

위와 같이 실행했을 때, 각 스레드의 결과는 정상 출력되지만 합산의 결과는 출력할 수 없습니다
왜냐하면 각 스레드를 main에서 실행하고 작업을 진행하는 동안 main 스레드는
이미 종료되었기 때문입니다

그렇기 때문에 아직 각 스레드에 반영되지 않은 결과를 출력하고 main스레드가 먼저 종료되기 때문에
정상적인 합산 값을 출력하지 못합니다

참고 - this

메소드를 호출하는 것은 특정 스레드가 호출하는 것입니다
스레드는 메소드의 호출을 관리하려고 메소드 단위의 스택 프레임을 만든 뒤,
해당 스택 프레임을 스택 위에 쌓아 올립니다

인스턴스의 메소드를 호출하면 어떤 인스턴스의 메소드를 호출했는지 기억하기 위해
인스턴스의 참조값을 스택 프레임 내부에 저장해둡니다
그리고 이것이 바로 this입니다

특정 메소드 안에서 this를 호출하면 스택프레임 안에 있는 this값을 불러서 사용합니다
즉, this는 호출된 인스턴스 메소드가 소속된 객체를 가리키는 참조이고
이것이 스택 프레임 내부에 저장되어 있습니다

해결방법은?

그렇다면 이러한 문제를 해결하기 위해서는 어떤 방법이 있을까요?

main스레드에 sleep을 지정한다

task를 수행하는 thread보다 긴 sleep을 main 스레드에 지정하는 방법입니다
하지만 이 방법은 작업의 종료를 예측할 수 없는 상황에서 선택할 수 없습니다

thread state가 TERMINATED될때까지 확인한다

이 방법은 반복문을 사용해서 각 스레드의 상태가 TERMINATED 될때까지
확인하는 방법입니다

논리적으로는 가능한 방법이지만, CPU연산을 계속 사용하게 되기때문에 그닥 좋은 방법은 아닙니다

join()을 사용한다

public class TestWaiting{

	public static void main(String[] args) throws InterruptedException {
		TaskSum task1 = new TaskSum(1, 100);
		Tastsum task2 = new TaskSum(101, 200);
		Thread thread1 = new Thread(task1, "thread1");
		Thread thread2 = new Thread(task2, "thread2");

		thread1.start();
		thread2.start();
		thread1.join();
		thread2.join();
		int sum = task1.res + task2.res;
		System.out.print(sum);
	}
	static class TaskSum implements Runnable {
		int start;
		int end;
		int res = 0;

		public TaskSum(int start, int end){
			this.start = start;
			this.end = end;
		}

		@Override
		public void run(){
			sleep(5000);
			int sum = 0;
			for(int i=start; i<=end; i++){
				sum += i;
			}
			res = sum;
			System.out.println("finish!!! " + sum);
		}
	}

}

이번에는 각 스레드에 join()을 추가했습니다
join()을 사용하면 InterruptedException을 예외로 던집니다

이제 메인스레드는 각 스레드가 아직 TERMINATED 상태가 아니라면 종료하지 않습니다
thread1.join() 코드에서 thread1이 종료될때까지 대기하다가 다음 코드로 이동합니다
thread2.join()도 동일합니다

WAITING (대기 상태)

스레드가 다른 스레드의 특정 작업이 완료되기를 무기한 기다리는 상태입니다

join()을 호출하는 스레드는 대상 스레드가 TERMINATED 상태가 될 때까지 대기합니다
대상 스레드가 TERMINATED 상태가 되면 호출 스레드는 다시 RUNNABLE 상태가 되면서
다음 코드를 수행합니다

하지만 이렇게 사용하면 한가지 문제가 또 존재합니다
다른 스레드가 완료될때까지 무기한 기다려야합니다

이러한 문제를 해결하기 위해 join은 한가지 기능을 더 제공합니다

특정 시간동안 대기하는 join()

public class TestWaiting{

	public static void main(String[] args) throws InterruptedException {
		TaskSum task1 = new TaskSum(1, 100);
		Tastsum task2 = new TaskSum(101, 200);
		Thread thread1 = new Thread(task1, "thread1");
		Thread thread2 = new Thread(task2, "thread2");

		thread1.start();
		thread2.start();
		thread1.join(7000);
		thread2.join(7000);
		int sum = task1.res + task2.res;
		System.out.print(sum);
	}
	static class TaskSum implements Runnable {
		int start;
		int end;
		int res = 0;

		public TaskSum(int start, int end){
			this.start = start;
			this.end = end;
		}

		@Override
		public void run(){
			sleep(5000);
			int sum = 0;
			for(int i=start; i<=end; i++){
				sum += i;
			}
			res = sum;
			System.out.println("finish!!! " + sum);
		}
	}

}

join()은 호출하는 스레드가 대상 스레드의 작업이 완료될때까지 무한정 대기하는 반면
위 코드에서 확인할 수 있듯이 join(ms)는 호출 스레드가 특정 시간만큼 대기합니다
지정한 시간이 지나면 다시 RUNNABLE 상태가 되어 다음 코드를 수행합니다

위 코드에서는 각각 TERMINATED가 될 때까지 7초씩 대기합니다
thread1에서는 5초정도 대기하겠지만, thread2에서는 이미 앞선 대기시간동안
작업이 완료되었기 때문에 대기 없이 바로 작업이 진행될 것입니다

드디어 우리가 원하는 두 작업의 합산을 main 스레드에서 확인할 수 있습니다!

참고

  • 김영한의 실전 자바 - 고급 1편
profile
Software Developer

0개의 댓글