| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Layered Architecture
- Transaction
- Adapter
- Hexagonal
- JDBC
- simplejpaRepository
- Spring Data JPA
- transactional
- Spring
- JPA
- hexagonal architecture
- 실무
- springboot
- Today
- Total
Ezcho
[Java] Thread와 상태제어 본문
1. Thread의 상태
Thread는 프로그램 실행의 가장 작은 단위이다. 우리가 이전 글에서는 MultiThread를 구현하는것을 보았는데,
그냥 저냥 프로그램을 실행시키면 그냥 main thread에서 실행하게 된다.
이런 Thread도 항상 실행 될 수가 없는것이. Thread도 한개의 프로세스이기 때문에, JVM에서 동시에 처리하는것이 아닌 시간을 잘게 쪼갠 후에 여러개의 쓰레드를 순서를 두고 실행하는것을 알 수 있다.
우리가 CPU내 프로세스의 상태를 나타낼 때
new, ready, running, blocked, suspended등으로 나타낼 수 있는데
JVM의 Thread역시 이렇게 현재 상태에 따라 여러 단계로 나뉠 수 있다.
현재 실행되는 Thread는
NEW: 쓰레드 객체가 생성되었으나, 아직 start()메서드가 호출되지 않은 상태
Runnable: 실행대기 상태
Running: 실행중 상태
Blocked: 봉쇄 상태
1. WAITING: 다른 쓰레드가 통지할 때 까지 기다리는 상태
2. TIMED_WAITING: Timesleep 상태
3. BLOCKED: 락이 풀릴때까지 기다리는 상태
Dead: 쓰레드의 run메서드가 종료된 상태
로 나눌 수 있다.
2. join() 메서드
쓰레드가 멈출 때 까지 기다리게 한다.
코드를 우선 보자
public class joinTest extends Thread{
public void run(){
for(int i = 0; i < 5; i++){
System.out.println("joinTest : "+ i);
try {
Thread.sleep(500); //0.5초씩 휴식
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class JoinExam {
public static void main(String[] args) {
joinTest thread = new joinTest();
thread.start();
System.out.println("Thread가 종료될때까지 기다립니다.");
try {
thread.join(); //join을 통해 joinTest의 running상태가 지속
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread가 종료되었습니다.");
}
}
main에서 joinTest 클래스 객체를 만들고 실행시키는데,
main역시 위에서 또 하나의 thread라고 설명했다. 즉 thread join이 없을 때, 아래와 같은 flow를 가지게 되는데,
| 시간(JVM에서 쪼갠) | code | main | joinTest |
| ... | (thread.start) | running | runable |
| 1 | runable | running | |
| 2 | running | runable | |
| 3 | runable | running |
.join()이라는 메서드를 사용한 후에는 joinTest의 running이 dead상태로 가기 전 까지 joinTest(thread객체)를 실행한다.
| 시간(JVM에서 쪼갠) | code | main | joinTest |
| 1 | thread.start(); | running | runable |
| 2 | thread.join(); | running | runable |
| 3 | ... | WAITING(blocked) | running |
| 4 | .. | WAITING(blocked) | running |
| 5 | ... | WAITING(blocked) | running |
3. wait()과 notify() 메서드
wait과 notify는 Synchronized된 블록 안에서 사용해야 한다.
wait 를 만나게 되면 모니터링 락의 권한을 놓고 대기하게 된다.
notify를 만나게 되면, 모니터링 락의 권한을 다시 내려놓는것을 의미한다.
public class ThreadB extends Thread{
int total;
public void run(){
synchronized(this){ //해당 동기화 블록이 모니터링 락을 획득한다.
for(int i=0; i<5 ; i++){
total += i;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify(); //main thread를 깨운다.
}
}
}
public class ThreadA {
public static void main(String[] args){
ThreadB b = new ThreadB();
b.start();
synchronized(b){
try{
System.out.println("b가 완료될때까지 기다립니다.");
b.wait();//wait을 만났다.
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Total: " + b.total);
}
}
}
표로 설명하면,
| time/code | main | ThreadB |
| ThreadB b = new ThreadB(); | running | NEW |
| b.start(); | running | runnable |
| synchronized(b){.. | running | runnable |
| b.wait(); | blocked (WAITING) | running |
| for();... //덧셈연산, threadB실행 | blocked (WAITING) | running |
| notify(); | running | dead |
| System.out.println("Total: ",b.total); | running | dead |
| dead | dead | |
wait 과 notify의 사용은 running 상태와 runnable상태로 쓰레드가 나뉘는것이 아니라, blocked 상태와 runnable, running 상태 간의 변화이다. wait()된 스레드는 반드시 notify()나 notifyAll()메소드를 호출하여 블록상태를 해제해 줄 필요가 있다는 점을 기억하자.

4. 데몬 쓰레드
데몬(Daemon)이란 보통 리눅스와 같은 유닉스계열의 운영체제에서 백그라운드로 동작하는 프로그램을 말한다.
쓰레드에 데몬을 설정하여 데몬 쓰레드로 바꾸어 주면 된다.
public class DaemonThread implements Runnable { //Runnable로 구현
public void run() {
while (true) {
System.out.println("데몬 쓰레드가 실행중입니다.");
try {
Thread.sleep(500); //0.5초씩 쉬면서 무한으로 데몬 쓰레드를 실행시킴
} catch (InterruptedException e) {
e.printStackTrace();
break; //Exception발생시 while을 탈출하게 함
}
}
}
public static void main(String[] args) {
Thread th = new Thread(new DaemonThread());
th.setDaemon(true); //th를 demon thread로 설정한다.
th.start(); //th실행
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("메인 쓰레드가 종료됩니다. "); //이때 Demonthread도 함께 종료됨
}
}
데몬쓰레드는 일반쓰레드(main)이 종료되면 강제적으로 종료되는 특징을 가지고 있다.
데몬쓰레드의 예로는 가비지컬랙션, 자동저장, 화면보호기 등이 있다.
실행 후 대기하고 있다가 어떤 조건이 만족되면 작업을 진행하고 작업이 종료되면 다시 대기하고 있는다.
'Java' 카테고리의 다른 글
| [Java] Thread (0) | 2022.11.25 |
|---|---|
| [java] java IO - Byte단위 입출력 (0) | 2022.11.16 |
| [java] 어노테이션 (0) | 2022.11.09 |
| [java] Date, Calendar클래스, Time 패키지 (0) | 2022.11.09 |
| [java] java.util패키지 (0) | 2022.11.02 |