JAVA chapter12. 멀티 스레드. 12.6 스레드 상태 제어
JAVA/CONCEPT 2017. 11. 29. 20:47 |chapter12. 멀티 스레드.
12.6 스레드 상태 제어
- 실행 중인 스레드의 상태를 변경하는 것을 말한다.
// 동영상을 종료 or 일시정지
- 상태 변화를 가져오는 메소드의 종류
메소드 | 설명 |
interrupt() | 일시 정지 상태의 스레드에서 InterruptedException 예외를 발생시켜, 예외 처리코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 한다 |
notify() notifyAll() | 동기화 블록 내에서 wait() 메소드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만든다 |
resume() | suspend() 메소드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만든다 – Deprecated (대신 notify(), notifyAll 사용) |
sleep(long millis) sleep(long millis, int nanos) | 주어진 시간 동안 스레드를 일시 정지 상태로 만든다. 주어진 시간이 지나면 자동적으로 실행 대기 상태가 된다 |
join() join(long millis) join(long millis, int nanos) | join() 메소드를 호출한 스레드는 일시 정지 상태가 된다. 실행 대기 상태로 가려면, join() 메소드를 멤버로 가지는 스레드가 종료되거나, 매개값으로 주어진 시간이 지나야 한다 |
wait() wait(long millis) wait(long millis, int nanos) | 동기화(synchronized) 블록 내에서 스레드를 일시 정지 상태로 만든다. 매개값으로 주어진 시간이 지나면 자동적으로 실행 대기 상태가 된다. 시간이 주어지지 않으면 notify(), notifyAll() 메소드에 의해 실행 대기 상태로 갈 수 있다 |
suspend() | 스레드를 일시 정지 상태로 만든다. resume() 메소드를 호출하면 다시 실행 대기 상태가 된다. - Deprecated (대신 wait() 사용) |
yield() | 실행 중에 우선순위가 동일한 다른 스레드에게 실행을 양보하고 실행 대기 상태가 된다. |
stop() | 스레드를 즉시 종료시킨다. - Deprecated |
12.6.1 주어진 시간동안 일시정지(sleep())
- 얼마 동안 일시정지 상태로 있을 것인지, 밀리세컨드(1/1000) 단위로 지정
- 일시정지 상태에서 interrupt() 메소드가 호출되면 InterruptedException 발생
3초 주기로 10번 비프음 발생
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package sec06.exam01_sleep; import java.awt.Toolkit; public class SleepExample { public static void main(String[] args) { Toolkit toolkit = Toolkit.getDefaultToolkit(); // 비프음을 발생시키는 메서드 for(int i=0; i<10; i++) { toolkit.beep(); try { Thread.sleep(3000); // 3초 동안 메인 스레드를 일시 정지 상태를 만듬 } catch(InterruptedException e) { } } } } | cs |
12.6.2 다른 스레드에게 실행 양보(yield())
// 1. 다음 예제에서는 처음 실행 후 3초 동안은 ThreadA와 ThreadB가 번갈아가며 실행된다.
// 2. 3초 뒤에 메인 스레드가 ThreadA의 work 필드를 false로 변경함으로써 ThreadA는 yield() 메소드를 호출한다
// 따라서 이후 3초 동안에는 ThreadB가 더 많은 실행 기회를 얻게 된다.
// 3. 메인 스레드는 3초 뒤에 다시 ThreadA의 work 필드를 true로 변경해서 ThreadA와 ThreadB가 번갈아가며 실행하도록 한다.
// 4. 마지막으로 메인 스레드는 3초 뒤에 ThreadA와 ThreadB의 stop 필드를 true로 변경해서 두 스레드가 반복 작업을 중지하고 종료하도록 한다.
ThreadA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package sec06.exam02_yield; public class ThreadA extends Thread { public boolean stop = false; public boolean work = true; public void run() { while(!stop) { if(work) { System.out.println("ThreadA 작업 내용"); } else { Thread.yield(); } } System.out.println("ThreadA 종료"); } } | cs |
ThreadB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package sec06.exam02_yield; public class ThreadB extends Thread { public boolean stop = false; public boolean work = true; public void run() { while(!stop) { if(work) { System.out.println("ThreadB 작업 내용"); } else { Thread.yield(); } } System.out.println("ThreadB 종료"); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package sec06.exam02_yield; public class YieldExample { public static void main(String[] args) { ThreadA threadA = new ThreadA(); // ThreadA, ThreadB 모두 실행 ThreadB threadB = new ThreadB(); threadA.start(); threadB.start(); try { Thread.sleep(3000); } catch (InterruptedException e) {} threadA.work = false; // ThreadB만 실행 try { Thread.sleep(3000); } catch (InterruptedException e) {} threadA.work = true; // ThreadA, ThreadB 모두 실행 try { Thread.sleep(3000); } catch (InterruptedException e) {} threadA.stop = true; // ThreadA, ThreadB 모두 종료 threadB.stop = true; // stop() 메소드는 강제 종료이기 때문에 프로그램이 불안정 상태일 수도있다. // 걍 필드값을 true로 만들어서 빨리 끝내게 한다 } } | cs |
12.6.3 다른 스레드의 종료를 기다림(join())
- 계산 작업을 하는 스레드가 모든 계산 작업을 마쳤을 때, 계산 결과값을 받아 이용하는 경우에 주로 사용
- join() 메소드를 실행하는 메소드가 일시정지가 되고, 다른 스레드의 종료를 기다린다.
1부터 100까지 합을 계산하는 스레드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package sec06.exam03_join; public class SumThread extends Thread { private long sum; public long getSum() { return sum; } public void setSum(long sum) { this.sum = sum; } public void run() { for(int i=1; i<=100; i++) { sum+=i; } } } | cs |
다른 스레드가 종료될 때까지 일시정지 상태를 유지
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package sec06.exam03_join; public class JoinExample { public static void main(String[] args) { SumThread sumThread = new SumThread(); sumThread.start(); System.out.println("1~100 합: " + sumThread.getSum()); // 계산이 끝나지도 않았는데, 출력해버리면 엉뚱한 값 나올수도 있음 try { sumThread.join(); // sumThread가 종료될 때까지 메인 스레드를 일시정지시킴 } catch (InterruptedException e) { } System.out.println("1~100 합: " + sumThread.getSum()); } } | cs |
12.6.4 스레드 간 협업(wait(), notify(), notifyAll())
// 경우에 따라서는 두 개의 스레드를 교대로 번갈아가며 실행해야 할 경우가 있다. 정확한 교대 작업이 필요할 경우,
// 자신의 작업이 끝나면 상대방 스레드를 일시 정지 상태에서 풀어주고, 자신은 일시정지 상태로 만드는것이다.
// 이 방법의 핵심은 공유 객체에 있다.
// 공유 객체는 두 스레드가 작업할 내용을 각각 동기화 메소드로 구분해 놓는다.
// 한 스레드가 작업을 완료하면 notify() 메소드를 호출해서 일시 정지 상태에 있는 다른 스레드를 실행 대기 상태로 만들고,
// 자신은 두 번 작업을 하지 않도록 wait() 메소드를 호출하여 일시 정지 상태로 만든다.
// 만약 wait() 대신 wait(long timeout)이나, wait(long timeout, int nanos)를 사용하면
notify()를 호출하지 않아도 지정된 시간이 지나면 스레드가 자동적으로 실행 대기 상태가 된다.
// notify() 메소드와 동일한 역할을 하는 notifyAll() 메소드도 있는데,
notify()는 wait()에 의해 일시 정지된 스레드 중 한 개를 실행 대기 상태로 만들고,
notifyAll()는 wait()에 의해 일시 정지된 모든 스레드들을 실행 대기 상태로 만든다.
// 이 메소드들은 Thread 클래스가 아닌 Object 클래스에 선언된 메소드이므로 모든 공유 객체에서 호출이 가능하다.
// 주의할 점은 이 메소드들은 동기화 메소드 또는 동기화 블록 내에서만 사용할 수 있다.
- 동기화 메소드 또는 블록에서만 호출 가능한 Object의 메소드
// Thread의 메소드가 아니다. 모든 객체가 가지고 있는 메소드이다
- wait()
// 호출한 스레드는 일시정지가 된다.
// 다른 스레드가 notify() 또는 notifyAll()을 호출하면 실행대기 상태가 된다
- wait(long timeout), wait(long timeout, int nanos)
// notify가 호출되지 않아도 시간이 지나면 스레드가 자동적으로 실행대기 상태가 된다
- 두개의 스레드가 교대로 번갈아 가며 실행해야할 경우에 주로 사용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package sec06.exam04_wait_notify; public class WorkObject { public synchronized void methodA() { System.out.println("ThreadA의 methodA() 작업 실행"); notify(); // 일시정지 상태에 있는 ThreadB를 실행 대기 상태로 만듬 try { wait(); // ThreadA를 일시정지 상태로 만듬 } catch (InterruptedException e) { } } public synchronized void methodB() { System.out.println("ThreadB의 methodB() 작업 실행"); notify(); // 일시정지 상태에 있는 ThreadA를 실행 대기 상태로 만듬 try { wait(); // ThreadB를 일시정지 상태로 만듬 } catch (InterruptedException e) { } } } | cs |
ThreadA
|
ThreadB
|
두 스레드를 생성하고 실행하는 메인 스레드
1 2 3 4 5 6 7 8 9 10 11 12 13 | package sec06.exam04_wait_notify; public class WaitNotifyExample { public static void main(String[] args) { WorkObject sharedObject = new WorkObject(); ThreadA threadA = new ThreadA(sharedObject); ThreadB threadB = new ThreadB(sharedObject); threadA.start(); threadB.start(); } } | cs |
두 스레드의 작업 내용을 동기화 메소드로 작성한 공유 객체
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 | package sec06.exam05_wait_notify; public class DataBox { private String data; public synchronized String getData() { // 공유 객체의 데이터 읽기 if(this.data == null) { try { wait(); } catch(InterruptedException e) {} } String returnValue = data; System.out.println("ConsummerThread가 읽은 데이터: " + returnValue); data = null; notify(); return returnValue; } public synchronized void setData(String data) { // 공유 객체에 데이터 저장 if(this.data != null) { try { wait(); } catch(InterruptedException e) {} } this.data = data; System.out.println("ProducerThread가 생성한 데이터: " + data); notify(); } } | cs |
데이터를 생산(저장)하는 스레드
|
데이터를 소비하는(읽는) 스레드
|
두 스레드를 생성하고 실행하는 메인 스레드
1 2 3 4 5 6 7 8 9 10 11 12 13 | package sec06.exam05_wait_notify; public class WaitNotifyExample { public static void main(String[] args) { DataBox dataBox = new DataBox(); ProducerThread producerThread = new ProducerThread(dataBox); ConsumerThread consumerThread = new ConsumerThread(dataBox); producerThread.start(); consumerThread.start(); } } | cs |
- stop 플래그를 이용하는 방법
// stop 플래그로 run() 메소드의 정상 종료를 유도한다 (일시정지는 종료는 못함)
무한 반복해서 출력하는 스레드
|
1초 후 출력 스레드를 중지시킴. 실행 클래스
|
* interrupt() 메소드를 이용하는 방법
- 일시정지 상태일 경우 InterruptedException 을 발생시킴
- 실행대기 또는 실행상태에서는 InterruptedException이 발생하지 않음 (무조건 일시정지 상태일때만 발생함)
- 일시정지 상태로 만들지 않고 while문을 빠져나오는 방법
무한 반복해서 출력하는 스레드
| 1초 후 출력 스레드를 중지시킴 (실행 클래스)
|
'JAVA > CONCEPT' 카테고리의 다른 글
JAVA chapter12. 멀티 스레드. 12.8 스레드 그룹 (0) | 2017.11.29 |
---|---|
JAVA chapter12. 멀티 스레드. 12.7 데몬 스레드 (0) | 2017.11.29 |
JAVA chapter12. 멀티 스레드. 12.5 스레드 상태 (0) | 2017.11.29 |
JAVA chapter12. 멀티 스레드. 12.4 동기화 메소드와 동기화 블록 (0) | 2017.11.29 |
JAVA chapter12. 멀티 스레드. 12.3 스레드 우선순위 (0) | 2017.11.29 |