-
[Java] 멀티 스레드 - 상태 & 상태 제어CSE/Java 2015. 12. 12. 16:11
멀티 스레드는 여러 절로 구성되어 있습니다.
스레드 상태
스레드 객체를 생성하고, start() 메소드를 호출하면 곧바로 스레드가 실행되는 것처럼 보이지만 사실은 대기 상태가 됩니다.
실행 대기 상태란 아직 스케줄링 되지 않아서 실행을 기다리고 있는 상태를 말합니다.
실행 대기 상태에 있는 스레드 중에서 스케줄링으로 선택된 스레드가 CPU를 점유하고 run() 메소드를 실행하는데, 이를 실행(Running) 상태라고 합니다. 실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 스케줄링에 의해 다시 실행 대기 상태로 돌아갈 수 있습니다.
실행 상태에서 run() 메소드가 종료되면, 더 이상 실행할 코드가 없기 때문에 스레드의 실행은 멈추게 됩니다. 이 상태를 종료 상태라고 합니다.
이러한 스레드의 상태를 코드에서 확인할 수 있도록 하기 위해 Java 5 부터 Thread 클래스에 getState() 메소드가 추가되었습니다. getState() 메소드는 다음 표처럼 스레드 상태에 따라서 Thread.State 열거 상수를 리턴합니다.
다음은 스레드의 상태를 출력하는 StatePrintThread 클래스입니다. 생성자 파라미터로 받은 타겟 스레드의 상태를 0.5 초 주기로 출력합니다.
* StatePrintThread.java
123456789101112131415161718192021222324252627282930package mT;public class StatePrintThread extends Thread {private Thread targetThread;public StatePrintThread(Thread targetThread) {this.targetThread = targetThread;}@Overridepublic void run() {while(true) {Thread.State state = targetThread.getState();System.out.println("타겟 스레드 상태: " + state);if (state == Thread.State.NEW) {targetThread.start();}if (state == Thread.State.TERMINATED) {break;}try {Thread.sleep(500);} catch (Exception e) {}}}}cs 다음은 타겟 스레드 클래스입니다. 20억 번 루핑을 돌게하여 RUNNABLE 상태를 유지하고 sleep() 메소드를 호출해서 1.5초간 TIMED_WAITING 상태를 유지합니다.
* TargetThread.java
1234567891011121314151617181920package mT;public class TargetThread extends Thread {@Overridepublic void run() {for (long i = 0; i < 2000000000; i++) {}try {Thread.sleep(1500);} catch (Exception e) {}for (long i = 0; i < 2000000000; i++) {}}}cs 다음은 StatePrintThread를 생성해서 파라미터로 전달받은 TargetThread의 상태를 출력하도록 작성된 실행 클래스입니다.
* ThreadStateExam.java
1234567891011package mT;public class ThreadStateExam {public static void main(String[] args) {StatePrintThread thread = new StatePrintThread(new TargetThread());thread.start();}}cs 스레드 상태 제어
다음 그림은 상태 변화를 가져오는 메소드의 종류를 보여줍니다.
위 표에서 wait(), notify(), notifyAll()은 Object 클래스의 메소드이고, 그 이외의 메소드는 모두 Thread 클래스의 메소드들입니다.
주어진 시간동안 일시 정지(sleep())
실행 중인 스레드를 일정 시간 멈추게 하고 싶다면 Thread() 클래스의 정적 메소드인 sleep()을 사용하면 됩니다. 파라미터로 얼마 동안 일시 정지 상태로 있을 것인지 밀리세컨드(1/1000) 단위로 시간을 주시면 됩니다.
다른 스레드에게 실행 양보(yield())
yield() 메소드를 호출한 스레드는 실행 대기 상태로 돌아가고 동일한 우선순위 또는 높은 우선순위를 갖는 다른 스레드가 실행 기회를 가질수 있도록 해줍니다.
다음 예제는 처음 실행 후 3초 동안 ThreadA와 ThreadB를 번갈아가며 실행됩니다. 3초 후에 메인 스레드가 ThreadA의 work 필드를 false로 변경함으로써 ThreadA는 yield() 메소드를 호출합니다. 따라서 이후 3초 동안은 ThreadB가 더 많은 실행 기회를 얻게 됩니다. 메인 스레드는 3초 뒤에 다시 ThreadA의 work 필드를 true로 변경하여 ThreadA와 B가 번갈아가며 실행하도록 합니다. 마지막으로 메인 스레드는 3초 뒤에 ThreadA와 ThreadB의 stop 필드를 true로 변경해서 두 스레드가 반복 작업을 중지하고 종료하도록 합니다.
* YieldExam.java
123456789101112131415161718192021222324package mT;public class YieldExam {public static void main(String[] args) {ThreadA threadA = new ThreadA();ThreadB threadB = new ThreadB();threadA.start();threadB.start();try { Thread.sleep(3000); } catch(InterruptedException e) {}threadA.work = false;try { Thread.sleep(3000); } catch(InterruptedException e) {}threadA.work = true;try { Thread.sleep(3000); } catch(InterruptedException e) {}threadA.stop = true;threadB.stop = true;}}cs * ThreadA.java
123456789101112131415161718192021package mT;public class ThreadA extends Thread {public boolean stop = false;public boolean work = true;@Overridepublic void run() {while(!stop) {if (work) {System.out.println("ThreadA 작업 내용");} else {Thread.yield();}}System.out.println("ThreadA 종료");}}cs * ThreadB.java
123456789101112131415161718192021package mT;public class ThreadB extends Thread {public boolean stop = false;public boolean work = true;@Overridepublic void run() {while(!stop) {if (work) {System.out.println("ThreadB 작업 내용");} else {Thread.yield();}}System.out.println("ThreadB 종료");}}cs 다른 스레드의 종료를 기다림(join())
스레드는 다른 스레드와 독립적으로 실행하는 것이 기본이지만 다른 스레드가 종료될 때까지 기다렸다가 실행해야 하는 경우가 발생할 수도 있습니다.
이런 경우에 join() 메소드를 제공합니다. join() 메소드를 호출하면 호출된 스레드가 종료될 때까지 일시 정지 상태로 대기하다가 실행이 됩니다.
다음 예제는 SumThread가 종료될 때까지 메인 스레드가 기달렸다가 출력하는 예제입니다.
* SumThread.java
12345678910111213141516171819202122package mT;public class SumThread extends Thread {private long sum;public long getSum() {return sum;}public void setSum(long sum) {this.sum = sum;}@Overridepublic void run() {for (int i = 1; i <= 100; i++) {sum += i;}}}cs * JoinExam.java
12345678910111213141516package mT;public class JoinExam {public static void main(String[] args) {SumThread thread = new SumThread();thread.start();try {thread.join(); // 메인 스레드가 해당 스레드의 종료까지 대기} catch (InterruptedException e) {}System.out.println("1 ~ 100 까지 합: " + thread.getSum());}}cs * 이 포스트은 서적 '이것이 자바다' 를 참고하여 작성한 포스트입니다.
'CSE > Java' 카테고리의 다른 글
[Java] 멀티 스레드 - 스레드풀(ThreadPool) (0) 2015.12.13 [Java] 멀티 스레드 - 데몬 스레드 & 스레드 그룹 (0) 2015.12.12 [Java] 멀티 스레드 - 상태 제어 (0) 2015.12.12 [Java] 멀티 스레드 - 우선순위, 동기화 메소드 (0) 2015.12.12 [Java] 멀티 스레드 - 작업 스레드 (0) 2015.12.12 [Java] 멀티 스레드 (0) 2015.12.12