남궁성님의 Java의 정석(3rd Edition)을 보고 정리한 글입니다.
1. 쓰레드의 상태와 실행제어
a. 쓰레드 상태
- 실행대기는 큐와 같은 구조로 먼저 실행대기열에 들어오면 먼저 실행된다.
- 실행 중에 suspend(),sleep(), wait(), join(), I/O Block에 의해 일시정지된 상태가 될 수 있다. (time-out), notifiy(), resume(), interrupt()에 의해 일시정지 상태가 끝나면 실행대기 상태가 된다.
상태 | 설명 |
NEW | 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태 |
RUNNABLE | 실행 중 또는 실행 가능한 상태 |
BLOCKED | 동기화블럭에 의해서 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태) |
WAITING, TIME_WAITING | 쓰레드의 작업이 종료되지는 않았지만 실행 가능하지 않은 일시정지 상태. TIME_WAITING은 일시정지시간이 지정된 경우를 의미 |
TERMINATED | 쓰레드의 작업이 종료된 상태 |
b. 쓰레드 실행제어
쓰레드 실행제어(스케줄링)할 수 있는 메서드가 제공된다.
메서드 | 설명 |
static void sleep(long millis) | 지정된 시간(ms)동안 쓰레드를 일시정지 시킨다. 지정 시간이 지나고 나면, 자동적으로 다시 실행대기상태가 된다. |
void join() void join(long millis) void join(long millis, int nanos) |
지정된 시간동안 쓰레드가 실행되도록 한다. 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다. |
interrupt() | sleep()이나 join()에 의해 일시정지상태인 쓰레드를 깨워서 실행대기상태로 만든다. 해당 쓰레드에서는 InterruptedException이 발생함으로써 일시정지상태를 벗어나게 한다. |
void stop() | 쓰레드를 즉시 종료시킨다. |
void suspend() | 쓰레드를 일시정지 시킨다. resume()을 호출하면 다시 실행대기상태가 된다. |
void resume() | suspend()에 의해 일시정지 상태에 있는 쓰레드를 실행대기상태로 만든다. |
static void yield() | 실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보하고 자신은 실행대기 상태가 된다. |
- resume(), stop(), suspend()는 쓰레드 교착상태로 만들기 쉽기 때문에 deprecated 됨.
* 교착 상태란 둘 이상의 스레드가 서로의 작업이 끝나기만을 기다리며 작업을 더 이상 진행하지 못하는 상태
2. 쓰레드의 실행제어 메서드 예제
sleep()
- 실행중인 쓰레드를 지정된 시간동안 멈추게 한다.
- 특정 쓰레드를 지정해서 멈추는 것은 불가능하다.
public class ThreadControl {
public static void main(String[] args) {
ThreadEx01 th1 = new ThreadEx01();
ThreadEx02 th2 = new ThreadEx02();
th1.start();
th2.start();
try {
// 에러가 나진 않지만 특정 쓰레드가 일시정지 되는 것이 아닌 실행중인 쓰레드가 일시정지
// th1.sleep(2000);
Thread.sleep(2000); // 현재 실행중인 쓰레드를 일시정지
} catch (InterruptedException e) {}
System.out.println("<<main 종료>>");
}
}
interrupt()
- interrupt를 설명할 수 있는 사용자 입력이 들어오면 종료되는 예제이다.
- sleep(), wait(), join()에 의해 일시정지 상태에 있을 때, 해당 쓰레드에 대해 interrupt()를 호출하면 대기상태인 쓰레드를 실행대기 상태로 만든다.
public class ThreadControl {
public static void main(String[] args) {
ThreadEx03 th1 = new ThreadEx03();
th1.start();
String input = JOptionPane.showInputDialog("아무값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + " 입니다.");
th1.interrupt(); // intrrupted 상태를 false로 변경
System.out.println("isInterrupted(): " + th1.isInterrupted());
}
}
class ThreadEx03 extends Thread {
@Override
public void run() {
int i = 10;
// i는 0이 아니고 isInterrupted true이면 카운트
// interrupt() 호출시 카운트는 종
while(i!=0 && !isInterrupted()) {
System.out.println(i--);
for(long x=0; x<2500000000L; x++); // 시간 지연
}
System.out.println("카운트 종료");
}
}
실행결과
10
9
8
7
6
5
4
입력하신 값은 15 입니다.
isInterrupted(): true
카운트 종료
suspend() + interrupt()+ stop() + resume() + yield()
- 남은 시간을 다음 쓰레드에게 양보하고, 자신(현재 쓰레드)은 실행대기한다.
- yield()와 interrupt()를 적절히 사용하면, 응답성과 효율을 높일 수 있다.
public class ThreadControl {
public static void main(String[] args) {
ThreadEx04 th1 = new ThreadEx04("*");
ThreadEx04 th2 = new ThreadEx04("**");
ThreadEx04 th3 = new ThreadEx04("***");
th1.start();
th2.start();
th3.start();
try {
Thread.sleep(2000);
th1.suspend();
Thread.sleep(2000);
th2.suspend();
Thread.sleep(3000);
th1.resume();
Thread.sleep(3000);
th1.stop();
th2.stop();
Thread.sleep(2000);
th3.stop();
} catch (InterruptedException e) {}
}
}
class ThreadEx04 implements Runnable {
boolean suspended = false;
boolean stooped = false;
Thread th;
ThreadEx04(String name) {
th = new Thread(this, name);
}
@Override
public void run() {
String name = th.getName();
while (!stooped) {
if (!suspended) {
System.out.println(name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(name + " - interrupted");
}
} else {
Thread.yield();
}
}
System.out.println(name + " - stopped");
}
public void suspend() {
suspended = true;
th.interrupt();
System.out.println(th.getName() + " - interrupt() by suspend()");
}
public void stop() {
stooped = true;
th.interrupt();
System.out.println(th.getName() + " - interrupt() by stop()");
}
public void resume() {
System.out.println(th.getName() + " - resume()");
suspended = false;
}
public void start() {
System.out.println(th.getName() + " - start()");
th.start();
}
}
실행결과
* - start()
** - start()
*** - start()
*
**
***
*
***
**
*
***
**
* - interrupted
* - interrupt() by suspend()
***
**
** - interrupt() by suspend()
** - interrupted
***
***
***
* - resume()
*
***
*
***
*
***
* - interrupted
* - interrupt() by stop()
** - interrupt() by stop()
* - stopped
** - stopped
***
***
*** - interrupt() by stop()
*** - interrupted
*** - stopped
join()
- 지정된 시간동안 특정쓰레드가 작업하는 것을 기다린다.
public class ThreadControl {
static long startTime = 0;
public static void main(String[] args) {
ThreadEx01 th1 = new ThreadEx01();
ThreadEx02 th2 = new ThreadEx02();
th1.start();
th2.start();
startTime = System.currentTimeMillis();
try {
th1.join(); // main 쓰레드가 th1의 작업이 끝날 때까지 기다린다.
th2.join(); // main 쓰레드가 th2의 작업이 끝날 때까지 기다린다.
} catch (InterruptedException e) {
}
System.out.println("소요시간: " + (System.currentTimeMillis() - startTime));
}
}
class ThreadEx01 extends Thread {
@Override
public void run() {
for (int i = 0; i < 300; i++) {
System.out.printf("%s", new String("-"));
}
}
}
//
class ThreadEx02 extends Thread {
@Override
public void run() {
for (int i = 0; i < 300; i++) {
System.out.printf("%s", new String("|"));
}
}
}
실행결과
|||||||||||||||||||||||||||----------||--------------------------------------------
-----------------------------------------------------------------------------------
-----------------------------|||||||||||||||||------||||||||||||||||||||---||||||||
||---||------------------------|||----------------|||||||||||||||||||||||||||||||||
|||||||||||------------------------------------------------||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||---------------------||-------------|||||||||||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|||||||||||||||||||소요시간: 18
'Programming > Java' 카테고리의 다른 글
[Java] 스트림(Stream) (0) | 2023.12.10 |
---|---|
[Java] 쓰레드(Thread) - 6(쓰레드 동기화) (0) | 2023.12.10 |
[Java] 쓰레드(Thread) - 4(데몬 쓰레드) (0) | 2023.12.10 |
[Java] 쓰레드(Thread) - 3(싱글쓰레드 vs. 멀티쓰레드) (0) | 2023.12.10 |
[Java] 쓰레드(Thread) - 2(쓰레드와 Stack Area) (0) | 2023.12.10 |