자바

JAVA 자바 Thread 자바 스레드 : 상태 제어 - 주어진 시간동안 일시 정지 sleep() / 다른 스레드에게 실행 양보 yield() / 다른 스레드의 종료를 기다림 join() / /// 스레드 간 협업 wait(), notify(), notifyAll()

알통몬_ 2017. 3. 15. 11:12
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 


주어진 시간동안 일시 정지 sleep()

 실행 중인 스레드를 일정 시간 멈추게 하고 싶을 때는 Thread 클래스의 static 메서드인 sleep() 을 사용하면됩니다. 

아래와 같이 Thread.sleep() 메서드를 호출한 스레드는 주어진 시간 동안 일시 정지 상태가 되고 

다시 실행 대기 상태로 돌아갑니다.

try{

   Thread.sleep(1000);

}catch(InterruptedException e){

  //interrupt() 메서드가 호출되면 실행

}

매개 값에는 일시 정지 시키고 싶은 시간 만큼 1000분의1초 단위로 값을 주면 됩니다. ex) 1000 => 1초

일시 정지 상태에서 주어진 시간이 되기전에 interrupt() 메서드가 호출되면

InterruptedException이 발생하기 때문에 예외처리가 필요합니다.


예제)

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(1500); // 1.5초동안 메인 스레드를 일시 정지 상태로 만듭니다

} catch(InterruptedException e) {

}

}

}

 

} 이 예제를 실행하면 1.5초마다 비프음이 들립니다. 10회


다른 스레드에게 실행 양보 yield() 

 스레드가 처리하는 작업은 반복적인 실행을 위해 for문이나 while문을 포함하는 경우가 많습니다.

가끔은 이 반복문들이 무의미한 반복을 하는 경우도 있습니다.

public void run() {

      while(true) {
        if(work) {

            System.out.println("Thread1 작업 내용");

        }

       }

     }

스레드가 시작되어 run() 메서드를 실행하면 while(true) {} 블록이 무한 루프를 돌게 됩니다. 

만약 work의 값이 false 라면 그리고 work 값이 false 에서 true로 변경되는 시점이 불분명하다면, 

while문은 어떤한 실행문도 실행하지 않고 무의미한 반복만을 하게 됩니다. 

이것보다는 다른 스레드에게 실행을 양보하고 자신은 실행 대기 상태로 가는 것이 프로그램 성능에 도움이 됩니다.

 이런 기능을 위해서 스레드는 yield() 메서드를 제공합니다.

 yield() 메서드를 호출한 스레드는 실행 대기 상태로 돌아가고 

동일한 우선순위나 높은 우선순위를 갖는 다른 스레드가 실행 기회를 가질 수 있도록 해줍니다.



 아래 코드는 의미 없는 반복을 줄이기 위해 yield() 메서드를 호출해서

 다른 스레드에게 실행 기회를 주도록 한 코드입니다.

public void run() {

      while(true) {
        if(work) {

            System.out.println("Thread1 작업 내용");

        } else {

            Thread.yield();

          }

       }

     }





다른 스레드의 종료를 기다림 join()

 스레드는 다른 스레드와 독립적으로 실행되는 것이 기본입니다. 

다른 스레드가 종료될 때까지 기다렸다가 실행해야 하는 경우도 있습니다. 

예를 들어 계산 작업을 하는 스레드가 모든 계산 작업을 마쳤을 때 계산 결과값을 받아 이용하는 경우가 그렇습니다. 

이런 경우를 위해 Thread는 join() 메서드를 제공합니다. 



예제를 보겠습니다.

1~100까지의 합을 구하는 예제)

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;

}

}

}

 


위 예제가 실행되는 동안 일시 정지 상태를 유지하는 예제)

public class JoinExample {

public static void main(String[] args) {

SumThread sumThread = new SumThread();

sumThread.start();

try {

sumThread.join();

} catch (InterruptedException e) {

} 이 부분을 주석처리하게 되면 정상적인 값이 아닌 다른 값이 나오게 됩니다.0 또는 

              컴퓨터의 성능에 따라 다른 값이 출력됩니다. 이유는 SumThread가 계산 작업을 

              완료하지 않은 상태에서 합을 먼저 출력하기 때문입니다.

System.out.println("1~100 합: " + sumThread.getSum());

}

}



========================================================================================================================================





스레드 간 협업 wait(), notify(), notifyAll()

정확한 교대 작업이 필요한 경우, 자신의 작업이 끝나면 상대방 스레드를 일시 정지 상태에서 풀어주고, 

자신은 일시 정지 상태로 만드는 것입니다. 이 방법의 핵심은 공유 객체에 있습니다. 

공유 객체는 두 스레드가 작업할 내용을 각각 동기화 메소드로 구분해 놓습니다. 

한 스레드가 작업을 완료하면 notify() 메서드를 호출해서 

일시 정지 상태에 있는 다른 스레드를 실행 대기 상태로 만들고 

자신은 두 번 작업을 하지 않도록 wait() 메서드를 호출하여 일시 정지 상태로 만듭니다.




wait() 대신에 wait(long timeout)  or  wait(long timeout, int nanos) 를 사용하게 되면 

notify()를 호출하지 않아도 지정된 시간이 지나면 스레드가 자동적으로 실행 대기 상태가 됩니다.

 notify() 메서드와 같은 역할을 하는 notifyAll() 메서드도 있습니다. 

notify()는 wait()에 의해 일시 정지된 스레드 중 한개를 대기 상태로 만들고, 

notifyAll() 메서드는 wait()에 의해 일시 정지된 모든 스레드들은 실행 대기 상태로 만듭니다. 

이 메서드들은 모든 공유 객체에서 호출이 가능하며,

 

*주의할 점 : 동기화 메서드 또는 동기화 블럭에서만 사용가능.


예제) WorkObject의 methodA() 와 methodB()에 정의해두고, 두 스레드 ThreadA,ThreadB가 교대로

methodA() 와 methodB()를 호출하도록 한 예제입니다.

공유 객체

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) {

}

}

 

}


methodA()를 실행하는 메서드

public class ThreadA extends Thread {

private WorkObject workObject;


public ThreadA(WorkObject workObject) {

this.workObject = workObject; // 공유 객체를 매개값으로 받아 필드에 저장

}

@Override

public void run() {

for(int i=0; i<10; i++) {

workObject.methodA();

}//공유 객체의 methodA()를 10번 반복

}

}

 


methodB()를 실행하는 메서드

public class ThreadB extends Thread {

private WorkObject workObject;


public ThreadB(WorkObject workObject) {

this.workObject = workObject;// 공유 객체를 매개값으로 받아 필드에 저장

}

@Override

public void run() {

for(int i=0; i<10; i++) {

workObject.methodB();

}//공유 객체의 methodB()를 10번 반복

}

 

}


실행 클래스

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(); 스래드 A,B 실행

}

 

}


반응형