sleep()
현재 스레드를 지정된 시간 동안 멈추게 한다.
static void sleep(long millis) // 천분의 일초 단위
static void sleep(long millis, int nanos) // 천분의 일초 + 나노초
sleep()에 의해 일시정지 상태가 된 스레드는 지정된 시간이 다 되거나 interrupt()가 호출되면(InterruptedException 발생) 깨어나 실행대기 상태가 된다.
따라서 예외 처리를 해야 한다. (InterruptedException이 발생하면 깨어남)
try {
Thread.sleep(1, 500000); // 스레드를 0.0015초 동안 멈추게 한다.
} catch (InterruptedException e) {}
매번 예외 처리를 하기 귀찮을 수 있다. 그러면 함수로 만들어 사용할 수 있다.
void delay(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {}
}
특정 스레드를 지정해서 멈추게 하는 것은 불가능하다. th1이라고 지정해도 이것은 th1을 멈추는 것이 아닌 현재의 스레드를 멈추게 하는 것이다.
따라서 헷갈리지 않도록 th1.sleep() 대신 Thread.sleep()를 사용하자
class ThreadEx12 {
public static void main(String args[]) {
ThreadEx12_1 th1 = new ThreadEx12_1();
ThreadEx12_2 th2 = new ThreadEx12_2();
th1.start();
th2.start();
try {
th1.sleep(2000); // th1을 2초동안 잠자게?
// Thread.sleep(2000) 이렇게 해야 오해의 여지가 없다.
} catch(InterruptedException e) {}
System.out.print("<<main 종료>>");
} // main
}
class ThreadEx12_1 extends Thread {
public void run() {
for(int i=0; i < 300; i++) {
System.out.print("-");
}
System.out.print("<<th1 종료>>");
} // run()
}
class ThreadEx12_2 extends Thread {
public void run() {
for(int i=0; i < 300; i++) {
System.out.print("|");
}
System.out.print("<<th2 종료>>");
} // run()
}
interrupt()와 interrupted()
대기상태(Watiting)인 스레드를 실행대기 상태(Runnable)로 만든다.
void interrupt() 스레드의 interrupted 상태를 false에서 true로 변경
boolean isInterrupted() 스레드의 interrupted 상태를 반환
static boolean interrupted() 현재 스레드의 interrrupted 상태를 알려주고, false로 초기화
스레드가 sleep(), wait(), join()에 의해 대기상태에 있을 때, 해당 스레드에 대해 interrupt()를 호출하면 대기상태에서 InterruptedException이 발생하고 스레드는 실행대기 상태(Runnable)로 바뀐다.
public static void main(String[] args) {
Thread th1 = new Thread();
th1.start();
...
th1.interrupt(); // interrupt()를 호출하면, interrupted 상태가 true가 된다.
...
System.out.println("isInterrupted() : " + th1.isInterrupted()); // true
}
interrupt()는 파일 다운로드 상황에서 사용할 수 있다. 다운로드를 취소하고자 할 때 취소 버튼을 누르면 interrupt()가 호출되게 한다.
class MyThread extends Thread {
public void run() {
...
while (downloaded && !isInterrupted()) {
// download를 수행한다.
...
}
System.out.println("다운로드가 끝났습니다.");
}
}
interrupt()가 호출되면 interrupted 상태가 true가 되는데 isInterrupted()에 !를 붙임으로써 !true -> false가 되어 반복문이 실행되지 않게 한다.
import javax.swing.JOptionPane;
class ThreadEx13_1 {
public static void main(String[] args) throws Exception {
ThreadEx13_2 th1 = new ThreadEx13_2();
th1.start();
String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
th1.interrupt(); // interrupt()를 호출하면, interrupted상태가 true가 된다.
System.out.println("isInterrupted():"+ th1.isInterrupted()); // true
// main 스레드가 interrupt 되었는지 확인
System.out.println("interrupted():"+ Thread.interrupted()); // false
}
}
class ThreadEx13_2 extends Thread {
public void run() {
int i = 10;
while(i!=0 && !isInterrupted()) {
System.out.println(i--);
for(long x=0;x<2500000000L;x++); // 시간 지연
}
System.out.println("isInterrupted():"+ this.isInterrupted()); // true
System.out.println("isInterrupted():"+ this.isInterrupted()); // true
// inInterrupted()와 달리 interrupted()는 interrupted라는 상태변수를 false로 초기화한다.
System.out.println("interrupted():"+ Thread.interrupted()); // true
System.out.println("interrupted():"+ Thread.interrupted()); // false
System.out.println("카운트가 종료되었습니다.");
} // main
}
interrupted()는 static 메서드이기 때문에 Thread.interrupted()로 사용해야 한다.
th1.interrupted()라고 했다고 해서 th1의 상태를 확인하는 것이 아니다.
해당 코드를 실행하는 스레드의 상태를 확인하는 것이다.
import javax.swing.JOptionPane;
class ThreadEx13_1 {
public static void main(String[] args) throws Exception {
ThreadEx13_2 th1 = new ThreadEx13_2();
th1.start();
String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
th1.interrupt(); // interrupt()를 호출하면, interrupted상태가 true가 된다.
System.out.println("isInterrupted():"+ th1.isInterrupted()); // true
}
}
class ThreadEx13_2 extends Thread {
public void run() {
int i = 10;
while(i!=0 && !isInterrupted()) {
System.out.println(i--);
for(long x=0;x<2500000000L;x++); // 시간 지연
}
System.out.println("카운트가 종료되었습니다.");
} // main
}
위 코드는 사용자의 입력이 끝나면 interrupt()에 의해 카운트다운이 중간에 멈춘다.
반면 아래 코드는 카운트가 종료되지 않는다.
import javax.swing.JOptionPane;
class ThreadEx14_1 {
public static void main(String[] args) throws Exception {
ThreadEx14_2 th1 = new ThreadEx14_2();
th1.start();
String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
th1.interrupt(); // interrupt()를 호출하면, interrupted상태가 true가 된다.
System.out.println("isInterrupted():"+ th1.isInterrupted());
}
}
class ThreadEx14_2 extends Thread {
public void run() {
int i = 10;
while(i!=0 && !isInterrupted()) {
System.out.println(i--);
try {
Thread.sleep(1000); // 1초 지연
} catch(InterruptedException e) {}
}
System.out.println("카운트가 종료되었습니다.");
} // main
}
차이점은 위 코드는 시간 지연을 for문으로 주었고 아래 코드는 sleep()으로 준 것이다.
앞서 sleep()에 의해 일시정지 상태가 된 스레드는 지정된 시간이 다 되거나 interrupt()가 호출되면(InterruptedException 발생) 깨어나 실행대기 상태가 된다고 했다. 이 과정에서 스레드의 interrupted 상태는 false로 자동 초기화 된다.
그래서 interrupt()가 호출되여 interrupted 상태가 true가 되었다가 InterruptedException이 발생해 다시 false로 초기화되어 while의 조건식 !isInterrupted()가 true가 되어 카운트다운이 종료되지 않고 계속된다. 따라서 앞서 말한 것처럼 sleep()은 예외처리를 해줘야 한다.
'JAVA' 카테고리의 다른 글
[Thread] join(), yield() (0) | 2023.02.27 |
---|---|
[Thread] suspend(), resume(), stop() (0) | 2023.02.27 |
[Thread] 데몬 스레드, 스레드의 상태 (0) | 2023.02.26 |
[Thread] 스레드의 우선순위 (0) | 2023.02.26 |
[Thread] 싱글 스레드와 멀티 스레드, 스레드의 I/O 블로킹 (0) | 2023.02.26 |