Threads Java

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class EarlyNotify extends Object {
  private List list;
  public EarlyNotify() {
    list = Collections.synchronizedList(new LinkedList());
  }
  public String removeItem() throws InterruptedException {
    synchronized (list) {
      while (list.isEmpty()) {
        print("wait()");
        list.wait();
        print("done with wait()");
      }
      String item = (String) list.remove(0);
      return item;
    }
  }
  public void addItem(String item) {
    print("entering");
    synchronized (list) {
      list.add(item);
      print("added: '" + item + "'");
      list.notifyAll();
      print("notified");
    }
    print("leaving");
  }
  private static void print(String msg) {
    String name = Thread.currentThread().getName();
    System.out.println(name + ": " + msg);
  }
  public static void main(String[] args) {
    final EarlyNotify enf = new EarlyNotify();
    Runnable runA = new Runnable() {
      public void run() {
        try {
          String item = enf.removeItem();
          print("returned: '" + item + "'");
        } catch (InterruptedException ix) {
          print("interrupted!");
        } catch (Exception x) {
          print("threw an Exception!!!\n" + x);
        }
      }
    };
    Runnable runB = new Runnable() {
      public void run() {
        enf.addItem("Hello!");
      }
    };
    try {
      Thread threadA1 = new Thread(runA, "A");
      threadA1.start();
      Thread.sleep(500);
      Thread threadA2 = new Thread(runA, "B");
      threadA2.start();
      Thread.sleep(500);
      Thread threadB = new Thread(runB, "C");
      threadB.start();
      Thread.sleep(1000);
      threadA1.interrupt();
      threadA2.interrupt();
    } catch (InterruptedException x) {
    }
  }
}