class FullException extends Exception {
    FullException(String msg) { super(msg); }
}

class Box<T> {
    private T[] items = (T[]) new Object[3];
    private int count = 0;

    synchronized void add(T item) throws FullException, InterruptedException {
        if (count == items.length) throw new FullException("Box is full");
        while (count == items.length) wait();
        items[count++] = item;
        notifyAll();
    }

    synchronized T retrieve() throws InterruptedException {
        while (count == 0) wait();
        T item = items[--count];
        notifyAll();
        return item;
    }

    boolean isEmpty() {
        return count == 0;
    }

    void clear() {
        count = 0;
    }
}

class Student {
    String name;
    Student(String name) { this.name = name; }
    public String toString() { return name; }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Box<Integer> intBox = new Box<>();
        Box<String> strBox = new Box<>();
        Box<Student> studentBox = new Box<>();

        Thread t1 = new Thread(() -> {
            try {
                intBox.add(1);
                intBox.add(2);
                intBox.add(3);
                System.out.println(intBox.retrieve());
            } catch (Exception e) { e.printStackTrace(); }
        });

        Thread t2 = new Thread(() -> {
            try {
                strBox.add("Hello");
                System.out.println(strBox.retrieve());
            } catch (Exception e) { e.printStackTrace(); }
        });

        Thread t3 = new Thread(() -> {
            try {
                studentBox.add(new Student("Alice"));
                System.out.println(studentBox.retrieve());
            } catch (Exception e) { e.printStackTrace(); }
        });

        t1.start();
        t2.start();
        t3.start();
    }
}
