「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 11)



Future 패턴



실행 결과를 얻는 데 시간이 걸리는 방법이 있다고 가정합니다. 실행 결과를 얻을 때까지 기다리는 대신에 교환권을 받는다. 상환권을 받는데 시간은 걸리지 않는다. 이 때의 교환권을 Future 역이라고 부른다. 교환권을 받은 스레드는 나중에 Future 역을 사용하여 실행 결과를 받으러 간다. 만약 실행 결과가 되어 있으면, 곧바로 그것을 받고, 완성되어 있지 않으면 가능한 때까지 기다리게 된다.

(코드 전체는 본서를 참조)

Main.java
public class Main { 
    public static void main(String[] args) { 
        ...
        Host host = new Host(); 
        Data data1 = host.request(10, 'A'); 
        Data data2 = host.request(20, 'B'); 
        Data data3 = host.request(30, 'C'); 

        try { 
            Thread.sleep(2000); 
        } catch (InterruptedException e) { 
        } 

        System.out.println("data1 = " + data1.getContent()); 
        System.out.println("data2 = " + data2.getContent()); 
        System.out.println("data3 = " + data3.getContent()); 
    } 
}

Host.java
public class Host { 
    public Data request(final int count, final char c) { 
        final FutureData future = new FutureData();  // 引換券を作成

        new Thread() { 
            public void run() { 
                RealData realdata = new RealData(count, c); 
                future.setRealData(realdata); 
            } 
        }.start(); 

        return future;  // 引換券を返す
    } 
}

Data.java
public interface Data { 
    public abstract String getContent(); 
}

Future.java
public class FutureData implements Data { 
    private RealData realdata = null; 
    private boolean ready = false; 
    public synchronized void setRealData(RealData realdata) { 
        if (ready) { 
            return;     // balk 
        } 
        this.realdata = realdata; 
        this.ready = true; 
        notifyAll(); 
    } 
    public synchronized String getContent() { 
        while (!ready) { 
            try { 
                wait(); 
            } catch (InterruptedException e) { 
            } 
        } 
        return realdata.getContent(); 
    } 
}

Host 클래스는 우선 FutureData의 인스턴스를 만든다. 이 인스턴스가 반환값이 된다. 다음으로 새로운 thread를 기동해, 그 안에서 RealData 의 인스턴스를 만든다. RealData의 인스턴스를 만드는 데는 시간이 걸린다. 일단 인스턴스가 생성되면 setRealData 메서드를 호출하여 이를 FutureData 인스턴스에 설정합니다.

FureData 클래스는, 교환권이 되는 클래스. realData 필드는, 이것으로부터 만들어지는 RealData 의 인스턴스를 보관 유지하는 필드. 이 필드는 setRealData 메소드에 의해 대입된다. getContent 메소드는, 실제의 데이터를 얻기 위한 메소드. setRealData 에 의해 realdata 가 세트 되는 것을 기다린다. 세트되어 있으면 빨리 돌아가고, 세트되어 있지 않으면 wait한다. wait로 기다리고 있는 thread는, setRealData안에서 호출하고 있는 notifyAll로 일어난다.

등장인물



클라이언트 역할
Client 역은 Host 역에 요청을 내고 결과(반환값)로서 즉시 VirtualData 역을 받는다. 여기서 받는 VirtualData 역은, 실제로는 Future 역. Client 역할은 반환 값이 RealData 역할인지 Future 역할인지 알 필요가 없습니다. 샘플 프로그램에서는 Main 클래스가 이 역할을 맡았다.

호스트 역할
Host 역할은 새로운 스레드를 만들 RealData 역할을 만들기 시작합니다. 한편, Client 역할은 Future 역할을 VirtualData 역할로 반환합니다. 샘플 프로그램에서는 Host 클래스가 이 역할을 맡았다.

VirtualData 역할
VirtualData역은 Future역과 RealData역을 동일시시키는 역할. 샘플 프로그램에서는 Data 인터페이스가 이 역할을 맡았다.

RealData 역할
RealData 역할은 실제 데이터를 나타내는 역할. 이 개체를 만드는 데는 시간이 걸립니다. 샘플 프로그램에서는 RealData 클래스가 이 역할을 맡았다.

Future 역할
Future 역은 RealData 역의 교환권으로 Host 역에서 Client 역으로 전달되는 역. Future 역은 Client 역에 대해서는 VirtualData 역으로서 행동한다. Client역에서 조작되었을 경우, RealData역이 완성될 때까지는, thread는 wait로 기다린다. 샘플 프로그램에서는 FureData 클래스가 이 역할을 맡았다.

생각을 넓히는 팁



기다릴 수 없는 Future 역할
getContent 메소드를 비동기적으로 구현하는 것도 가능. getContent 메소드 안에서 새로운 thread를 만든다고 하는 까다로운 이야기는 아니고, Balking 패턴을 사용해, 「완료되어 있지 않으면 일단 돌아간다」 하도록(듯이) 하면 된다.

다시 전화 요청할 및 Future 패턴
처리 완료를 기다리고 반환 값을 얻고 싶을 때 콜백이라는 방법도 생각할 수 있습니다. 콜백이라고 하는 것은, 처리가 완료했을 때에 Host역이 기동한 thread가, Client역의 메소드를 호출하는 방법이다.

관련
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 1)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 2)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 3)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 4)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 5)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 6)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 7)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 8)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 9)
「Java 언어로 배우는 디자인 패턴 (멀티 스레드 편)」정리 (그 10)

좋은 웹페이지 즐겨찾기