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



Producer-Consumer 패턴



producer(생산자)는 데이터를 작성하는 스레드, consumer(소비자)는 데이터를 이용하는 스레드. 생산자와 소비자가 서로 다른 스레드로 움직일 때, 양자의 처리 속도의 어긋남이 문제가 된다. 소비자가 데이터를 수신하려고 할 때 데이터가 아직 생성되지 않았거나 생산자가 데이터를 전달하려고 할 때 소비자가 데이터를 수신 할 수있는 상태가 아닐 수도 있습니다. Producer-Consumer 패턴에서는, 생산자와 소비자의 사이에 「교도역」이 들어간다. 이 다리 역할은 스레드 사이의 처리 속도의 어긋남을 채 웁니다. 생산자와 소비자 모두가 단수인 경우 파이프 패턴이라고 할 수 있습니다.

3명의 수탉이 케이크를 만들어 테이블에 놓고, 그것을 3명의 손님이 먹는, 테이블에는 3개까지 케이크를 둘 수 있다는 예를 생각한다. 테이블 위에 3개의 케이크가 놓여 있는데, 콕이 테이블에 더 케이크를 두려고 했다면, 콕은 테이블에 둘 곳이 생길 때까지 기다린다. 테이블 위에 1개도 케이크가 놓여 있지 않을 때, 손님이 테이블에서 케이크를 취하려고 하면, 손님은 케이크가 놓일 때까지 기다린다.

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

MakerThread
public class MakerThread extends Thread { 
    ...
    private static int id = 0; // ƒケーキ通し番号(全コックで共通)
    ...
    public void run() { 
        try { 
            while (true) { 
                Thread.sleep(random.nextInt(1000)); 
                String cake = "[ Cake No." + nextId() + " by " + getName() + " ]"; 
                table.put(cake); 
            } 
        } catch (InterruptedException e) { 
        } 
    } 
    private static synchronized int nextId() { 
        return id++; 
    } 
}

EaterThread

public class EaterThread extends Thread { 
    ...
    public void run() { 
        try { 
            while (true) { 
                String cake = table.take(); 
                Thread.sleep(random.nextInt(1000)); 
            } 
        } catch (InterruptedException e) { 
        } 
    }
    ...

public class Table { 
    private final String[] buffer; 
    private int tail;  // 次にputする場所
    private int head;  // 次にtakeする場所
    private int count; // buffer内のケーキ数
    ...
    // ケーキを置く
    public synchronized void put(String cake) throws InterruptedException { 
        System.out.println(Thread.currentThread().getName() + " puts " + cake); 
        while (count >= buffer.length) { 
            wait(); 
        } 
        buffer[tail] = cake; 
        tail = (tail + 1) % buffer.length; 
        count++; 
        notifyAll(); 
    } 
    // ケーキを取る
    public synchronized String take() throws InterruptedException { 
        while (count <= 0) { 
            wait(); 
        } 
        String cake = buffer[head]; 
        head = (head + 1) % buffer.length; 
        count--; 
        notifyAll(); 
        System.out.println(Thread.currentThread().getName() + " takes " + cake); 
        return cake; 
    } 
}

실행 결과
...
MakerThread-1 puts [ Cake No.10 by MakerThread-1 ]
EaterThread-1 takes [ Cake No.10 by MakerThread-1 ]
MakerThread-1 puts [ Cake No.11 by MakerThread-1 ]
EaterThread-3 takes [ Cake No.11 by MakerThread-1 ]
MakerThread-3 puts [ Cake No.12 by MakerThread-3 ]
MakerThread-3 puts [ Cake No.13 by MakerThread-3 ]
EaterThread-2 takes [ Cake No.12 by MakerThread-3 ]
EaterThread-3 takes [ Cake No.13 by MakerThread-3 ]
...

등장인물



데이터 역할:
Producer 역에 의해 작성되어 Consumer 역에 의해 이용된다. 위의 예에서 케이크가 이것에 해당합니다.
Producer 역할:
데이터 역할을 만들고 채널 역할에 전달합니다. 위의 예에서는 MakerThread 클래스가 이것에 해당한다.
Consumer 역할:
Channel 역으로부터 Data 역을 받아 이용한다. 위의 예에서는, EaterThread 클래스가 이것에 해당한다.
채널 역할:
Producer 역으로부터 Data 역을 받아, 보관, Consumer 역으로부터의 요구에 응해 Data 역을 건네준다. Producer 역과 Consumer 역으로부터의 액세스에 대해서 배타 제어를 실시한다. Channel 역은 Producer 역과 Consummer 역 사이에 들어가 Data 역을 건네주는 중계 지점의 역할, 통신로의 역할을 한다. 위의 예에서는, Table 클래스가 이것에 해당한다.

생각을 넓히는 팁


  • 멀티 thread의 동작에의 배려를 실시하고 있는 코드는, 모두 Channel역인 Table 클래스안에 숨겨져 있다.
  • Producer 역은 Consumer 역의 처리의 진행 상태에 좌우되지 않는다.

  • InterruptedException이라는 예외가 throw될 가능성이 있다는 것은, 「시간이 걸리는」메소드인, 「캔슬할 수 있는」메소드라고 말할 수 있다.

  • thread가 wait로 기다리고 있는 경우에도, sleep와 같은 것으로 캔슬할 수 있다. interrupt 메소드를 사용하면, wait 하고 있는 thread에 대해서, 「이제 notify/notifyAll를 기다리지 않아도 된다. 그러나, wait의 경우에는, 락에 주의할 필요가 있다. 스레드는 가중치 세트에있을 때 일단 잠금을 해제했습니다. wait중에 interrupt를 불린 thread(즉, 캔슬 된 thread)는, 락을 재취득하고 나서 InterruptedException를 던진다. 즉, 락이 잡힐 때까지는 예외 InterruptedException를 던질 수 없다.

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

    좋은 웹페이지 즐겨찾기