import java.util.concurrent.*;
import java.util.concurrent.*;

public class ProducerConsumerQueue {
    public static void main(String[] args) throws Exception {
        ArrayBlockingQueue<Integer> c = new ArrayBlockingQueue<Integer>(1);
        
        Producer p1 = new Producer(c, 1);
        Producer p2 = new Producer(c, 2);
        Consumer c1 = new Consumer(c, 1);
        Consumer c2 = new Consumer(c, 2);

        p1.start();
        p2.start();
        
        c1.start();
        c2.start();

        p1.join();
        p2.join();
        
        System.out.println("\r\nAll producers have completed");
        System.out.println("<enter> to exit");
        System.in.read();
        
        c1.interrupt();
        c2.interrupt();
        
    }

    public static class Producer extends Thread {
        private BlockingQueue<Integer> cubbyhole;
        private int id;
                    
        public Producer(BlockingQueue<Integer> c, int id) {
            super("P"+id);
            cubbyhole = c;
            this.id = id;
        }

        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    int value = id*100 + i;
                    System.out.format("%s puts: %d ?%n", Thread.currentThread().getName(), value);  
                    cubbyhole.put(value);
                    System.out.format("%s puts: %d !%n", Thread.currentThread().getName(), value);  
                    sleep((int)(Math.random() * 200));
                } catch (InterruptedException ex) { 
                    System.out.format("%s %s%n", Thread.currentThread().getName(), ex);  
                    return;
                }
            }
        }
    }

    public static class Consumer extends Thread {
        private BlockingQueue<Integer> cubbyhole;
        private int id;
            
        public Consumer(BlockingQueue<Integer> c, int id) {
            super("C"+id);
            cubbyhole = c;
            this.id = id;
        }

        public void run() {
            int count = 0;
            for (;;) {
                try {
                    System.out.format("%s gets: ... ?%n", Thread.currentThread().getName());  
                    int value = cubbyhole.take();
                    System.out.format("%s gets: %d !%n", Thread.currentThread().getName(), value);  
                    count += 1;
                    sleep((int)(Math.random() * 100));
               
                } catch (InterruptedException ex) { 
                    System.out.format("%s %s%n", Thread.currentThread().getName(), ex);  
                    return;
                }        
            }
        }
    }
}

