이것이 자바다 Chap12

1. Thread.sleep은 어떤 스레드를 정지시키는 것인가?

package ex1_yield;
public class ex1_yieldex { // yield
    public static void main(String[] args) {
        ex1_ThreadA threadA = new ex1_ThreadA();
        ex1_ThreadB threadB = new ex1_ThreadB();

        threadA.start();
        threadB.start();

        System.out.println("---------------------------------------------------------------");
        try {
            Thread.sleep(3);
        } catch (Exception e) {
            // TODO: handle exception
        }
        System.out.println("-----------------------------------------------------------------");
        threadA.work = false;
        try {
            Thread.sleep(3);
        } catch (Exception e) {
            // TODO: handle exception
        }

        System.out.println("-----------------------------------------------------------------");
        threadA.work = true;
        try {
            Thread.sleep(3);
        } catch (Exception e) {
            // TODO: handle exception
        }

        System.out.println("-------------------------------------------------------------");
        threadA.stop = true;
        threadB.stop = true;

    }
}
package ex1_yield;
public class ex1_ThreadA extends Thread{
    public boolean stop = false;
    public boolean work = true;

    public void run() {
        while (!stop) {
            if (work) {
                System.out.println("ThreadA description : ");
            } else {
                Thread.yield();
            }
        }
        System.out.println("ThreadA exited");
    }
}

ThreadA와 같은 내용의 ThreadB가 있을 때. Main() 에서 Thread.sleep(300)을 하더라도 ThreadA 와 Thread B에서 계속에서 출력값이 출력되는 것을 볼 수 있다. Sleep함수는 어떤 Thread를 정지시키는가?

=> 현재 진행중인 스레드를 정지 시킨다. Main에서 실행 시 Main Thread, 각 Thread class 속에서 호출 시 각 Thread class 들이 정지된다.  그래서 예제에서 Sleep을 하더라도 ThreadA, B 는 멈추지 않고 계속 실행됨을 알 수 있음. 단지 Main Thread 에서 boolean값을 변경하는 사이에 시간을 두기 위해서 Sleep을 사용되었다. Main Thread에서 sleep된 시간만큼 멈추고 다음 코드가 진행되는 것을 볼 수 있다. 만약 ThreadA에서 sleep을 하고 싶다면 Thread A 클래스 내부에서 sleep을 진행시켜야 한다

Java Chap12-ex4_wait_notify

package ex4_wait_nofity_ex;

public class DataBox {
    private String data;

    public synchronized String getData(){               //consuummer thread에서 이용하는 method.. consummer - wait , productor - notify
                                                        //data 가 null 이면 읽지마!
        if(this.data == null){
            try {
                wait();
            } catch (Exception e) {
                //TODO: handle exception
            }
        }
        String returnValue = data;
        System.out.println("ConsummerThread가 읽은 데이터 : " + returnValue);
        this.data = null;                                    //data field 를 null로 만들고 productor thread를 실행 대기 상태로 만듦
        notify();
        return returnValue;
    }

    public synchronized void setData(String data){      //producer thread에서 이용하는 method... producer - wait, consummer = notify
                                                        //data가 써져 있으면 적지 마!
        if(this.data != null){
            try {
                wait();
            } catch (Exception e) {
                //TODO: handle exception
            }
        }
        this.data = data;
        System.out.println("producer thread가 생성한 데이터 : " + data);
        notify();                                       //data field에 값을 저장하고 consummer thread를 실행 대기 상태
    }
}
package ex4_wait_nofity_ex;

public class ConsumerThread extends Thread {
    private DataBox dataBox;

    public ConsumerThread(DataBox dataBox) {
        this.dataBox = dataBox;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            // TODO Auto-generated method stub
            //System.out.println("Consummer Thread : " + getState());
            String data = dataBox.getData();
            System.out.println(data);
            //System.out.println("Consummer Thread : " + getState());
        }
    }
}
package ex4_wait_nofity_ex;

public class ProducerThread extends Thread {
    private DataBox dataBox;

    public ProducerThread(DataBox dataBox) {
        this.dataBox = dataBox;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            // TODO Auto-generated method stub
            // System.out.println("Producer Thread : " + getState());

            String data = "Data - " + i;
            dataBox.setData(data);

            // System.out.println("Producer Thread : " + getState());
        }
    }
}
package ex4_wait_nofity_ex;
//위 예제와 같은 맥락으로 한번씩 진행되지만 만약 이전 사항이 진행되지 않았으면 다음을 진행하지 않는 형태

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();
    }
}

=> getState함수로 확인 했을 때, 출력되는 상태의 Thread는 항상 Runnable 상태. 여기서 run함수 진행 중 어디에서 다른 스레드로 넘어갈지 모른다. 그래서 wait이나 notify함수를 사용할때에는 스레드가 넘어가지 않는다는 보장을 받기 위해서 snycronized 된 블록 또는 메서드 안에서만 쓸 수 있는 것 같다.

예제에서 Producer에서 시작해서 setData 전에 Consumer Thread로 넘어갈 수 있다. 만약 consumer로 넘어가면 data가 없는 상태이므로 wait 했다가 notify 되므로 그 사이에 producer쓰레드로 넘어가게된다. 반대의 경우도 마찬가지이다.

==> 처음에는 위 처럼 생각했었는데 한줄씩 생각해봤을 때 이해가 안가는 부분이 많았다. 결과부터 말하자면 위에 처럼 이해한 내용은 틀렸다. Sycronized 된 블록 내에서 스레드가 넘어가지 않는다고 말했는데 wait()이 호출되면 그 스레드는 그 즉시 멈추고 공유 객체(?) 에 대한 lock이 풀리게된다. 여기서는 데이터가 없는 상태에서 consumer 스레드가 진행된다면 wait()이 호출되고 그 즉시 consumer 스레드가 정지한다. 여기서는 스레드가 두개 뿐이므로 바로 producer 스레드가 진행되고 setData() 에 notify를 통해 consumer 스레드가 다시 큐에 올라가게 된다.!! 이어서 진행이 아니라 다시 큐에 올라간다. break와 비슷한 느낌이다.

==>wait() 과 sleep()의 차이는 synchronized 락이 풀리냐 안풀리냐의 차이이다! wait은 break되는 느낌으로 일시 정지 후 다른 스레드가 호출되는데 sleep은 락이 풀리지 않고 일정 시간이 지난 후 다시 시행된다.

업데이트:

댓글남기기