import java.lang.ref.*;

class Foo 
{
   static volatile int gen = 0;
   public int id;
   public Foo()
   {
      id = gen++;
   }
   protected void finalize() throws Throwable
   {
      System.out.println("finalizing: " + id);
   }
}

public class refqueue implements Runnable
{
   static ReferenceQueue queue = new ReferenceQueue();

   static Object lock = new Object();
   static Foo enqueued = null;
   static PhantomReference weak;

   public static void createObj()
   {
      if (enqueued == null)
      {
         System.out.println("enqueued == null");
         Foo foo = new Foo();
         weak = new PhantomReference(foo, queue);
      }
      else
      {
         System.out.println("enqueued != null");
         weak = new PhantomReference(enqueued, queue);
         enqueued = null;
      }
   }

   public static void main(String[] args) throws Exception
   {
      refqueue ref = new refqueue();
      new Thread(ref).start();
      while (true)
      {
         Thread.sleep(2000);
         synchronized (lock)
         {
            createObj();
            System.gc();
            lock.wait();
         }
      }
   }

   public void run()
   {
      while (true)
      {
         try
         {
            System.out.println("waiting for queue....");
            Reference ref = queue.remove();
            System.out.println("removed returned");
            Foo foo = (Foo)ref.get();
            System.out.println("queue got: " + foo.id);
            Thread.sleep(2000);
            System.out.println("enqueue: " + foo.id);
            enqueued = foo;
            synchronized (lock)
            {
               lock.notify();
            }
         }
         catch (Exception ex) 
         {
            ex.printStackTrace();
         }
      }
   }
}
