title: Java Concurrency JDK 1.6: Busy wait does better than signalling? Effective Java #51
body: <p>Joshua Bloch's "Effective Java", Item 51 is not about depending on the thread scheduler as well as not keeping threads unnecessarily in the runnable state. Quoted text:</p>
  
  <blockquote>
    <p>The main technique for keeping the number of runnable threads down is to have each thread 
    do a small amount of work and then wait for some condition using Object.wait or for some 
    time to elapse using Thread.sleep. Threads should not busy-wait, repeatedly checking a data 
    structure  waiting  for  something  to  happen.  Besides  making  the  program  vulnerable  to  the 
    vagaries  of  the  scheduler,  busy-waiting  can  greatly  increase  the  load  on  the  processor, 
    reducing the amount of useful work that other processes can accomplish on the same machine.</p>
  </blockquote>
  
  <p>And then goes on to show a microbenchmark of a busy wait vs using signals properly. In the book, the busy wait does 17 round trips/s whereas the wait/notify version does 23,000 round trips per second.</p>
  
  <p>However, when I tried the same benchmark on JDK 1.6, I see just the opposite - the busy wait does 760K roundtrips/second whereas the wait/notify version does 53.3K roundtrips/s - that is, wait/notify should have been ~1400 times faster, but turns out to be ~13 times slower?</p>
  
  <p>I understand the busy waits aren't good and signalling is still better - cpu utilization is ~50% on the busy wait version whereas it stays at ~30% on the wait/notify version - but is there something that explains the numbers?</p>
  
  <p>If it helps, I'm running JDK1.6 (32 bit) on Win 7 x64 (core i5).</p>
  
  <p><strong>UPDATE</strong>: Source below. To run the busy work bench, change the base class of PingPongQueue to BusyWorkQueue
      import java.util.LinkedList;
      import java.util.List;</p>
  
  <pre><code>abstract class SignalWorkQueue { 
      private final List queue = new LinkedList(); 
      private boolean stopped = false; 
  
      protected SignalWorkQueue() { new WorkerThread().start(); } 
  
      public final void enqueue(Object workItem) { 
          synchronized (queue) { 
              queue.add(workItem); 
              queue.notify(); 
          } 
      } 
  
      public final void stop()  { 
          synchronized (queue) { 
              stopped = true; 
              queue.notify(); 
          } 
      } 
      protected abstract void processItem(Object workItem) 
          throws InterruptedException; 
      private class WorkerThread extends Thread { 
          public void run() { 
              while (true) {  // Main loop 
                  Object workItem = null; 
                  synchronized (queue) { 
                      try { 
                          while (queue.isEmpty() &amp;&amp; !stopped) 
                              queue.wait(); 
                      } catch (InterruptedException e) { 
                          return; 
                      } 
                      if (stopped) 
                          return; 
                      workItem = queue.remove(0); 
                  } 
                  try { 
                      processItem(workItem); // No lock held 
                  } catch (InterruptedException e) { 
                      return; 
                  } 
              } 
          } 
      } 
  }
  
  // HORRIBLE PROGRAM - uses busy-wait instead of Object.wait! 
  abstract class BusyWorkQueue {
      private final List queue = new LinkedList();
      private boolean stopped = false;
  
      protected BusyWorkQueue() {
          new WorkerThread().start();
      }
  
      public final void enqueue(Object workItem) {
          synchronized (queue) {
              queue.add(workItem);
          }
      }
  
      public final void stop() {
          synchronized (queue) {
              stopped = true;
          }
      }
  
      protected abstract void processItem(Object workItem)
              throws InterruptedException;
  
      private class WorkerThread extends Thread {
          public void run() {
              final Object QUEUE_IS_EMPTY = new Object();
              while (true) { // Main loop
                  Object workItem = QUEUE_IS_EMPTY;
                  synchronized (queue) {
                      if (stopped)
                          return;
                      if (!queue.isEmpty())
                          workItem = queue.remove(0);
                  }
  
                  if (workItem != QUEUE_IS_EMPTY) {
                      try {
                          processItem(workItem);
                      } catch (InterruptedException e) {
                          return;
                      }
                  }
              }
          }
      }
  }
  
  class PingPongQueue extends SignalWorkQueue {
      volatile int count = 0;
  
      protected void processItem(final Object sender) {
          count++;
          SignalWorkQueue recipient = (SignalWorkQueue) sender;
          recipient.enqueue(this);
      }
  }
  
  public class WaitQueuePerf {
      public static void main(String[] args) {
          PingPongQueue q1 = new PingPongQueue();
          PingPongQueue q2 = new PingPongQueue();
          q1.enqueue(q2); // Kick-start the system
  
          // Give the system 10 seconds to warm up
          try {
              Thread.sleep(10000);
          } catch (InterruptedException e) {
          }
  
          // Measure the number of round trips in 10 seconds
          int count = q1.count;
          try {
              Thread.sleep(10000);
          } catch (InterruptedException e) {
          }
          System.out.println(q1.count - count);
  
          q1.stop();
          q2.stop();
      }
  }
  </code></pre>