[기본 개념] 8 | (1.4) 스레드 실행제어
1 프로세스와 쓰레드
2 쓰레드의 구현과 실행
3 start( )와 run( )
4 싱글쓰레드와 멀티쓰레드
5 쓰레드의 우선순위
6 쓰레드 그룹(thread group)
7 데몬 쓰레드(daemon thread)
8> 쓰레드의 실행제어
9 쓰레드의 동기화
9.1 synchronized를 이용한 동기화
9.2 wait( )와 notify( )
9.3 Lock과 Condition을 이용한 동기화
9.4 volatile
9.5 fork & join 프레임웍
8. 쓰레드의 실행제어 (2)
suspend( ), resume( ), stop( )
suspend( )는 sleep( )처럼 쓰레드를 멈추게 한다. resume( )을 호출해야 다시 실행 대기상태가 되며, stop( )은 호출되는 즉시 쓰레드가 종료된다.
suspend( )와 stop( )이 교착상태를 일으키기 쉽게 작성되어있으므로 되도록 지양해야 한다.
예제/ThreadEx17.java
public class ThreadEx17 {
public static void main(String[] args) {
ThreadEx17_1 th1 = new ThreadEx17_1("*");
ThreadEx17_1 th2 = new ThreadEx17_1("**");
ThreadEx17_1 th3 = new ThreadEx17_1("***");
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 ThreadEx17_1 implements Runnable {
volatile boolean suspended = false;
volatile boolean stopped = false;
Thread th;
ThreadEx17_1(String name) {
th = new Thread(this, name); // Thread(Runnable r, String name)
}
public void run() {
while (!stopped) {
if (!suspended) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
System.out.println(Thread.currentThread().getName() + " - stoopped");
}
public void suspend() { suspended = true; }
public void resume() { suspended = false; }
public void stop() { stopped = true; }
public void start() { th.start(); }
}
실행결과
*
***
**
**
*
***
***
**
**
***
***
***
***
*
***
*
***
*
***
** - stoopped
* - stoopped
***
***
*** - stoopped
yield( ) - 다른 쓰레드에게 양보한다.
yield( )는 쓰레드 자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보한다.
yield( )와 interrupt( )를 적절히 사용하면, 프로그램의 응답성을 높이고 효율적인 실행이 가능하다.
예제/ThreadEx18.java
public class ThreadEx18 {
public static void main(String[] args) {
ThreadEx18_1 th1 = new ThreadEx18_1("*");
ThreadEx18_1 th2 = new ThreadEx18_1("**");
ThreadEx18_1 th3 = new ThreadEx18_1("***");
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 ThreadEx18_1 implements Runnable {
volatile boolean suspended = false;
volatile boolean stopped = false;
Thread th;
ThreadEx18_1(String name) {
th = new Thread(this, name); // Thread(Runnable r, String name)
}
public void run() {
String name = th.getName();
while (!stopped) {
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 + " - stoopped");
}
public void suspend() {
suspended = true;
th.interrupt();
System.out.println(th.getName() + " - interrupt() by suspend()");
}
public void stop() {
stopped = true;
th.interrupt();
System.out.println(th.getName() + " - interrupt() by stop()");
}
public void resume() { suspended = false; }
public void start() { th.start(); }
}
실행결과
*
**
***
**
*
***
**
*
* - interrupted
***
* - interrupt() by suspend()
**
***
** - interrupt() by suspend()
** - interrupted
***
***
***
*
***
*
***
*
***
* - interrupt() by stop()
** - interrupt() by stop()
* - interrupted
* - stoopped
** - stoopped
***
***
*** - interrupt() by stop()
*** - interrupted
*** - stoopped
yield( )을 호출해서 남은 실행시간을 while문에서 낭비하지 않고 다른 쓰레드에게 양보하게 되므로 더 효율적이다.
stop( )이 호출되었을 때, Thread.sleep(1000)에 의해 일시정지에 머물러 있을 상황에서 stopped값이 true로 바꿔도 쓰레드가 정지될 때까지 최대 1초의 시간 지연이 생길 것이다. 이때, interrupt( )를 호출하면, sleep( )에서 InterruptedException이 발생하여 즉시 일시정지 상태를 벗어난다.
join( ) - 다른 쓰레드의 작업을 기다린다.
쓰레드 자신이 하던 작업을 잠시 멈추고 다른 쓰레드가 지정된 시간 동안 작업을 수행하도록 할 때, 즉 작업 중에 다른 쓰레드의 작업이 먼저 수행되어야 할 때 사용한다.
join( )도 sleep( )처럼 interrupt( )에 의해 대기상태에서 벗어날 수 있으며, sleep( )처럼 try-catch문으로 감싸야한다. sleep( )과 다른 점은 join( )은 현재 쓰레드가 아닌 특정 쓰레드에 대해 동작하므로 static메서드가 아니다.
예제/ThreadEx20.java
public class ThreadEx20 {
public static void main(String[] args) {
ThreadEx20_1 gc = new ThreadEx20_1();
gc.setDaemon(true);
gc.start();
int requiredMemory = 0;
for (int i = 0; i < 20; i++) {
requiredMemory = (int) (Math.random() * 10) * 20;
// 필요한 메모리가 사용할 수 있는 양보다 크거나 전체 메모리의 60%이상 사용했을 경우 gc깨운다.
if (gc.freeMemory() < requiredMemory
|| gc.freeMemory() < gc.totalMemory() * 0.4) {
gc.interrupt(); // 잠자고 있는 gc를 깨운다.
try {
gc.join(100);
} catch (InterruptedException e) {}
}
gc.usedMemory += requiredMemory;
System.out.println("usedMemory : " + gc.usedMemory);
}
}
}
class ThreadEx20_1 extends Thread {
final static int MAX_MEMORY = 1000;
int usedMemory = 0;
public void run() {
while (true) {
try {
Thread.sleep(10 * 1000); // 10초를 기다린다.
} catch (InterruptedException e) {
System.out.println("Awaken by interrupt().");
}
gc(); // garbage collection을 수행한다.
System.out.println("Garbage Collected. Free Memory : " + freeMemory());
}
}
public void gc() {
usedMemory -= 300;
if (usedMemory < 0) usedMemory = 0;
}
public int totalMemory() { return MAX_MEMORY; }
public int freeMemory() { return MAX_MEMORY - usedMemory; }
}
실행결과
usedMemory : 0
usedMemory : 160
usedMemory : 240
usedMemory : 280
usedMemory : 280
usedMemory : 420
usedMemory : 520
usedMemory : 560
usedMemory : 560
usedMemory : 720
Awaken by interrupt().
Garbage Collected. Free Memory : 580
usedMemory : 480
usedMemory : 540
usedMemory : 660
Awaken by interrupt().
Garbage Collected. Free Memory : 640
usedMemory : 420
usedMemory : 600
usedMemory : 680
Awaken by interrupt().
Garbage Collected. Free Memory : 620
usedMemory : 420
usedMemory : 540
usedMemory : 560
usedMemory : 680
join( )을 이용해서 main쓰레드의 작업이 수행하는 중간에 쓰레드 gc가 작업할 시간(gc.join(100))을 주어야 원활히 수행된다.
가비지 컬렉터와 같은 데몬 쓰레드의 우선순위를 낮추기보다는 sleep( )을 이용해서 주기적으로 실행되도록 하다가 필요할 때마다 interrupt( )를 호출해서 즉시 가비지 컬렉션이 이루어지도록 하는 것이 좋다. 그리고 필요시 join( )도 함께 사용해야 한다.
출처 | Java의 정석 (남궁 성)
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[기본 개념] 8 | (1.6) 스레드 동기화(Lock, Condition, Volatile, Fork&Join 프레임웍 (0) | 2022.01.24 |
---|---|
[기본 개념] 8 | (1.5) 스레드 동기화(Synchronized, wait( ), notify( )) (0) | 2022.01.23 |
[기본 개념] 8 | (1.3) 스레드 실행제어 (0) | 2022.01.22 |
[기본 개념] 8 | (1.2) 스레드 우선순위, 스레드 그룹, 데몬 스레드 (0) | 2022.01.20 |
[기본 개념] 8 | (1.1) 프로세스, 스레드, Start( ), Run( ), 싱글/멀티 스레드 (0) | 2022.01.18 |