JAVA/HIGH JAVA

[JAVA] 스레드 - 데몬스레드/상태/yield/종료

아잠만_ 2024. 4. 30. 11:40

데몬 스레드( daemon Thread)

  • 일반 스레드의 작업을 돕는 보조적인 역할을 수행
  • 일반 스레드가 모두 종료되면 자동적으로 종료
  • 가비지 컬렉터, 자동저장, 화면자동갱신 등에 사용된다
  • 무한루프와 조건문을 이용하여 실행 후 대기하다가 특정조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다
  • setDaemon(boolean on)은 반드시 start()를 호출되기 전에 실행
    그렇지 않으면 IllegalThreadStateException이 발생

자동 저장 기능 구현 예제

public class ThreadTest08 {
	public static void main(String[] args) {
		
		Thread autoSave = new AutoSaveThread();
		
		System.out.println("데몬스레드 여부 : "+autoSave.isDaemon()); // 데몬스레드 여부 : false
		
		// 데몬스레드로 설정하기
		// 반드시 start() 메서드 호출 이전에 설정해야 한다
		autoSave.setDaemon(true); 
		
		System.out.println("데몬스레드 여부 : "+autoSave.isDaemon()); // 데몬스레드 여부 : true
		
		autoSave.start();
		
		try {
			for(int i=1; i<=20; i++) {
				System.out.println(i);
				Thread.sleep(1000);
			}
		} catch (InterruptedException e) {
			// TODO: handle exception
		}
		
		System.out.println("main 스레드 종료...");
	}
}

// 자동 저장하는 스레드 (3초에 한 번씩 자동으로 저장하는 스레드)
class AutoSaveThread extends Thread{
	// 작업 내용을 저장하는 메서드
	public void save() {
		System.out.println("작업 내용을 저장합니다...");	// 저장한다고 가정
	}
	
	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				// TODO: handle exception
			}
			save();
		}
	}
	
}

스레드의 상태


// 스레드의 상태를 출력하는 예제
public class ThreadTest09 {

	public static void main(String[] args) {
		TargetThread target = new TargetThread();
		Thread t = new StatePrintThread(target);
		t.start();
	}
}

// 스레드 상태의 검사 대상이 되는 스레드
class TargetThread extends Thread {

	@Override
	public void run() {
		long k = 0;
		for (long i = 1L; i <= 20_000_000_000L; i++) {
			k = k + 1;
		}

		try {
			Thread.sleep(1500);
		} catch (InterruptedException e) {
			// TODO: handle exception
		}

		k = 0;
		for (long i = 1L; i <= 20_000_000_000L; i++) {
			k = k + 1;
		}
	}
}

// 검사 대상 스레드의 상태 값을 출력하는 스레드
class StatePrintThread extends Thread {
	private TargetThread target;

	// 생성자 (초기화)
	public StatePrintThread(TargetThread target) {
		this.target = target;
	}

	@Override
	public void run() {
		Thread.State state = null;
		long startTime = 0L;
		long endTime = 0L;
		while (true) {
			// 스레드의 상태값 구하기 ==> getState()메서드 이용
			// 스레드의 상택값은 enum(열거형)으로 선언되어있다.
			if (state != target.getState()) {
				if (startTime != 0L) {
					endTime = System.currentTimeMillis();
					System.out.println(endTime - startTime);
				}
				state = target.getState();
				System.out.println("TargetThread의 상태 값 : " + state);
				startTime = System.currentTimeMillis();
			}
			if (state == Thread.State.NEW) {// 스레드 상태가 NEW 상태이면
				target.start(); // 검사 대상 스레드 실행
			}

			if (state == Thread.State.TERMINATED) {
				break; // 반복문 탈출
			}
//			try {
//				Thread.sleep(500);
//			} catch (InterruptedException e) {
//				// TODO: handle exception
//			}
		}
	}
}
TargetThread의 상태 값 : NEW
0
TargetThread의 상태 값 : RUNNABLE
4387
TargetThread의 상태 값 : TIMED_WAITING
1515
TargetThread의 상태 값 : RUNNABLE
4386
TargetThread의 상태 값 : TERMINATED

yield()메서드

다른 스레드에게 실행을 양보하는 메서드


import java.io.ObjectInputStream.GetField;

public class ThreadTest10 {
	public static void main(String[] args) {
		YieldTest th1 = new YieldTest("1번 스레드");
		YieldTest th2 = new YieldTest("2번 스레드");
		th1.start();
		th2.start();
		
		try {
			Thread.sleep(10);	// 일시정지
		} catch (InterruptedException e) {
			// TODO: handle exception
		}
		
		System.out.println("--------------------------------------------111");
		th1.work = false;
		
		try {
			Thread.sleep(10);	// 일시정지
		} catch (InterruptedException e) {
			// TODO: handle exception
		}
		System.out.println("--------------------------------------------222");
		
		th1.work = true;
		try {
			Thread.sleep(10);	// 일시정지
		} catch (InterruptedException e) {
			// TODO: handle exception
		}
		System.out.println("--------------------------------------------333");
		th1.stop = true;
		th2.stop = true;
	}
}

// yield() 메서드 연습용 스레드
class YieldTest extends Thread{
	public boolean stop = false;
	public boolean work = true;

	// 생성자
	public YieldTest(String name) {
		super(name);	// 부모의 생성자 호출 (Thread) - 스레드의 이름 설정하기
	}
	
	@Override
	public void run() {
		while(!stop) {
			if(work) {
				// this.getName()과 같음 >  Thread 부모 클래스에 있는 것
				System.out.println(getName() + " 작업 중...");
			} else {
				System.out.println(getName() + " 양보...");
				Thread.yield();	// 아무 작업이 없을 시 사용 - 다른 스레드에게 실행 양보
			}
			
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO: handle exception
			}
		}
	}

	
}

예제


3개의 스레드가 각각 알파벳을 A~Z까지 출력하는데 출력이 끝낸 순서대로 결과를 나타내는 프로그램 작성하기
import java.util.Random;

public class ThreadTest11 {
	public static void main(String[] args) {
		DisplayCharacter[] disArr = new DisplayCharacter[] { new DisplayCharacter("신짱구"), new DisplayCharacter("김철수"),
				new DisplayCharacter("신짱아") };

		for (DisplayCharacter dis : disArr) {
			dis.start();
		}

		for (DisplayCharacter dis : disArr) {
			try {
				dis.join();
			} catch (InterruptedException e) {
				// TODO: handle exception
			}
		}
		
		System.out.println();
		System.out.println("\t경기 결과");
		System.out.println("순 위 : " + DisplayCharacter.strRank);
	}
}

// A ~ Z까지 출력하는 스레드
class DisplayCharacter extends Thread {
	public static String strRank = ""; // 작업을 끝낸 순서대로 이름이 저장될 변수
	private String name;

	// 생성자
	public DisplayCharacter(String name) {
		this.name = name;
	}

	@Override
	public void run() {
		for (char c = 'A'; c < 'Z'; c++) {
			System.out.println(name + "의 출력 문자 : " + c);
			try {
				Thread.sleep(new Random().nextInt(500));
			} catch (InterruptedException e) {
				// TODO: handle exception
			}
		}
		System.out.println(name + "의 출력 작업 끝...");
		// 출력이 끝난 스레드의 이름을 strRank변수에 추가한다
		strRank += name + " ";
	}

}

stop() / interrupt()

package kr.or.ddit.basic;

/*
 *  Thread의 stop()메서드를 호출하면 해당 스레드가 바로 멈춘다.
 *  이 때 사용하던 자원을 정리하지 못하고 프로그램이 종료되어 다른 프로그램에 영향을 줄 수 있다
 *  그래서 stop()메서드는 비추천으로 되어있다
 */

public class ThreadTest13 {
	public static void main(String[] args) {
//		ThreadStopTest01 th1 = new ThreadStopTest01();
//		th1.start();
//		
//		try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			// TODO: handle exception
//		}
////		th1.stop();
//		th1.setStop(true);	// 프로그램 자체에서 정지

		// interrupt() 메서드를 이용한 스레드 멈추기
		ThreadStopTest02 th2 = new ThreadStopTest02();
		th2.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO: handle exception
		}
		th2.interrupt(); 
		// 일시정지상태에서 interrupt() 메서드가 실행되면 interruptedException발생 

	}

}

// 스레드를 멈추게하는 연습용 스레드
class ThreadStopTest01 extends Thread {
	private boolean stop;

	public void setStop(boolean stop) {
		this.stop = stop;
	}

	@Override
	public void run() {
		while (!stop) {
			System.out.println(" 스레드 실행 중...");
			// ....

			// ...
		}

		System.out.println("자원정리...");
		System.out.println("스레드 종료...");
	}

}

// interrupt() 메서드를 이용하여 스레드를 멈추게 하는 방법
class ThreadStopTest02 extends Thread {
	@Override
	public void run() {
		// 방법1 ==> interrupt메서드와 sleep메소드를 이용한 방법
//		try {
//			while (true) {
//				System.out.println("Thread 실행중...");
//				Thread.sleep(1);
//			}
//		} catch (InterruptedException e) {
//			// TODO: handle exception
//		}
		
		// 방법2 ==> interrupt()메서드가 호출되었는지 검사하는 메서드를 이용하는 방법
		while(true) {
			System.out.println("스레드 작업중");
			
			// 검사방법1 ==> Thread의 인스턴스 메서드인 isInterrupted()메서드 이용하기
			// isInterrupted()메서드는 interrupt()메서드가 호출되면 true를 반환한다
//			if(this.isInterrupted()) {
//				break;
//			}
			
			// 검사방법2 ==> Thread의 정적메서드인 interrupted()메서드 이용하기
			// 			이 메서드도 interrupt()메서드가 호출되면 true를 반환한다
			if(Thread.interrupted()) {
				break;
			}
		}
		System.out.println("자원정리...");
		System.out.println("스레드 종료...");
	}
}