TOP
본문 바로가기
프로그래밍 및 기타/Java, Spring Boot

[java] 스레드 제어

by 기록자_Recordian 2025. 4. 10.
728x90
반응형
이전 내용
 

[java] java 스레드(thread) - 멀티, 메인, 작업 / 동기화 메소드

이전 내용 [java] java.lang 패키지이전 내용 [java] 예외 (클래스, 처리), 다중 catch, 예외 떠넘기기, String args[]이전 내용 [java] 인터페이스 상속, 중첩 클래스, 중첩 인터페이스이전 내용 [java] instanceo

puppy-foot-it.tistory.com


스레드 제어

 

스레드를 생성하고 시작하면 스레드는 다양한 상태를 가지게 되는데, 스레드의 상태는 자동으로 변경될 수도 있고, 코드에 의해 변경될 수도 있다.

스레드 객체 생성 후 start() 메소드를 호출하면 바로 실행되는 것이 아니라 실행 대기 상태(언제든 실행될 준비가 되어 있는 상태)가 된다. 실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 다시 실행 대기 상태로 돌아갈 수 있고, 실행 대기 상태에 있는 다른 스레드가 선택되어 실행 상태가 되기도 한다.

 

[스레드 상태의 상세 설명]
스레드는 여러 상태를 가지며, 각 상태는 실행하는 과정에서의 특징을 나타내고 있다. 이를 통해 프로그램의 흐름과 성능을 효과적으로 관리할 수 있다.

1. 새로운 상태 (New)

  • 스레드 객체가 생성되었지만 아직 실행되지 않은 상태.
  • Thread thread = new Thread();와 같이 스레드를 생성하는 코드가 실행되면, 해당 스레드는 새로운 상태에 있다.
Thread thread = new Thread(new Runnable() {
    public void run() {
        // 스레드가 실행할 코드
    }
});

 

2. 실행 대기 상태 (Runnable)

  • 스레드가 실행될 준비가 되어 있고, CPU에 의해 실행되기를 기다리는 상태.
  • 이 상태는 CPU가 다른 스레드를 실행 중이라면 해당 스레드는 대기.
  • 스레드는 start() 메서드가 호출되면 run() 메서드를 호출하며, 자동으로 이 상태로 전환.
thread.start(); // 스레드 시작

 

3. 실행 중 상태 (Running)

  • 스레드가 CPU에서 실제로 실행되고 있는 상태.
  • 이 상태에서는 스레드가 할당된 작업을 수행하며, CPU의 자원을 직접적으로 사용.

4. 대기 상태 (Blocked)

  • 스레드가 특정 자원(예: 파일, 데이터베이스)에 접근할 수 없어서 대기하는 상태.
  • 다른 스레드가 자원을 점유하고 있을 때 발생. 이 상태에서 스레드는 해당 자원이 해제되기를 기다리고, 해제되면 다시 실행 대기 상태로 전환.
  • 스레드를 일시적으로 대기시키기 위해 sleep(milliseconds) 메서드를 사용하여 지정된 시간 동안 실행을 멈추게 할 수 있다.

※ 밀리세컨드(1/1000초)

try {
    Thread.sleep(1000); // 1초 대기
} catch (InterruptedException e) {
    e.printStackTrace();
}

 

5. 준비 상태 (Waiting)

  • 스레드가 다른 스레드의 작업 완료 또는 특정 신호를 기다리는 상태.
  • Object.wait() 또는 Thread.join() 메서드가 호출되면 해당 스레드는 준비 상태로 전환되어, 신호가 올 때까지 대기.
public synchronized int getData() throws InterruptedException {
    while (!isDataAvailable) {
        wait(); // 데이터가 준비될 때까지 대기
    }
    isDataAvailable = false;
    return data;
}

 

6. 종료 상태 (Terminated)

  • 스레드의 실행이 완료되어 더 이상 실행할 작업이 없는 상태.
  • 스레드는 자연스럽게 종료되거나, 예외에 의해 비정상적으로 종료될 수 있다.
  • interrupt() 메소드는 스레드가 일시 정지 상태에 있을 때 InterruptedException을 발생시키는 역할을 하며, 이를 이용하면 run() 메소드를 정상 종료할 수 있다. ▶ 즉시 실행되는 것이 아니라, 미래에 일시 정지 상태가 되면 멈춘다.
thread.interrupt(); // 스레드 강제 종료

예제1

 

Q. 1부터 100까지 구하기

- 1부터 100까지 구하는 스레드 생성

- 메인에서는 총합 출력

- 100까지 다 구하면 종료되도록 설정

public class SumThread extends Thread {
	int total;

	@Override
	public void run() {
		synchronized (this) {
			for (int i = 1; i <= 100; i++) {
				total += i;
				System.out.println(i + "을 더한 총합: " + total);
			}
			System.out.println("총합: " + total);
		}
	}
}
public class SumTest {

	public static void main(String[] args) {
		SumThread s = new SumThread();
		s.start();
		synchronized (s) {
			try {
				System.out.println("스레드 s가 끝날때까지 대기..");
				s.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 


예제2

 

Q. 생산자-소비자 문제

- 생산자(Producer) 스레드가 1부터 5까지의 숫자를 하나씩 생성해 공유 변수에 저장하고, 소비자(Consumer) 스레드가 그 숫자를 읽어 출력하는 프로그램 작성.

- 단, 생산자가 값을 생성한 후 소비자가 읽을 때까지 대기하고, 소비자가 값을 읽은 후 소비자가 다음 값을 생성하도록 wait()와 notify()를 사용해 동기화

  • wait(): 현재 스레드를 다른 스레드가 이 객체에 대한 notify() 또는 notifyAll() 메소드를 호출할 때까지 대기
  • notify() : 이 객체에 대해 대기중인 스레드 하나를 깨움
  • notifyAll() : 이 객체에 대해 대기중인 모든 스레드를 깨움
public class SharedResource {
	private int sharedValue = 0;
	private boolean isValueProduced = false;

	// 생산자 메서드
	public synchronized void produce(int value) throws InterruptedException {
		while (isValueProduced) { // 이미 값이 생산된 상태 - 소비자가 값을 읽을 때까지
			System.out.println("생산자: 값이 이미 존재하여 대기 중..");
			wait(); // 현재 객체(this)의 모니터에서 대기
		}
		// 값 생산
		sharedValue = value;
		isValueProduced = true;
		System.out.println("생산자: " + value + " 생산 완료");

		// 소비자에게 알림
		notify();
	}

	// 소비자 메서드
	public synchronized void consume() throws InterruptedException {
		// 값이 생산되지 않은 경우 생산자가 생산 시까지 대기
		while (!isValueProduced) {
			System.out.println("소비자: 값이 없어 대기 중..");
			wait();
		}

		// 값 소비
		System.out.println("소비자: " + sharedValue + " 소비 완료");
		isValueProduced = false;

		// 생산자에게 알림
		notify();
	}
}
public class ResourceTest {

	public static void main(String[] args) {
		// 공유 자원 가진 객체 생성
		SharedResource resource = new SharedResource();

		// 생산자 스레드
		Thread producer = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 5; i++) {
					try {
						resource.produce(i);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});

		// 소비자 스레드
		Thread consumer = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 5; i++) {
					try {
						resource.consume();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});

		// 스레드 실행
		producer.start();
		consumer.start();
	}
}


예제3

 

Q. 은행 계좌 입출금 시스템

[요구 사항]

1. BankAccount 클래스

- 속성: balance (정수, 계좌 잔액)

- 메서드: a. deposit(int amount): 입금 / b. withdraw(int amount): 출금

- 동기화: 잔액 변경 시 스레드 간 충돌 방지

 

2. DepositThread와 WithdrawThread 클래스

- Runnable 인터페이스 구현

- 각각 입금과 출금을 반복 수행 (예. 5회씩)

 

3. 메인 클래스: BankApp

- 초기 잔액 1000원으로 계좌 생성

- 입금 스레드와 출금 스레드 각각 실행

- 최종 잔액 출력

public class BankAccount {
	private int balance = 1000; // 잔액 (정수)

	// 입금 메서드
	public synchronized void deposit(int amount) throws InterruptedException {
		balance += amount;
		System.out.println("입금: " + amount + ", 잔액: " + balance);
	}

	// 출금 메서드
	public synchronized void withdraw(int amount) throws InterruptedException {
		balance -= amount;
		System.out.println("출금: " + amount + ", 잔액: " + balance);
	}

	// 잔액
	public int getBalance() {
		return balance;
	}

	public int setBalance() {
		return balance;
	}
}
public class BankApp {

	public static void main(String[] args) {
		BankAccount bankacc = new BankAccount();

		// 입금 스레드 생성
		Thread deposit = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 5; i++) {
					try {
						bankacc.deposit(200);
						Thread.sleep(200);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});

		// 출금 스레드 생성
		Thread withDraw = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 5; i++) {
					try {
						bankacc.withdraw(300);
						Thread.sleep(200);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});

		// 스레드 시작
		deposit.start();
		withDraw.start();

		// 스레드 종료까지 대기
		try {
			deposit.join();
			withDraw.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("최종 잔액: " + bankacc.getBalance());
	}
}


다음 내용

 

 

 

728x90
반응형