title: java concurrency: many writers, one reader
body: <p>I need to gather some statistics in my software and i am trying to make it fast and correct, which is not easy (for me!) </p>
  
  <p>first my code so far with two classes, a StatsService and a StatsHarvester</p>
  
  <pre><code>public class StatsService
  {
  private Map&lt;String, Long&gt;   stats   = new HashMap&lt;String, Long&gt;(1000);
  
  public void notify ( String key )
  {
      Long value = 1l;
      synchronized (stats)
      {
          if (stats.containsKey(key))
          {
              value = stats.get(key) + 1;
          }
          stats.put(key, value);
      }
  }
  
  public Map&lt;String, Long&gt; getStats ( )
  {
      Map&lt;String, Long&gt; copy;
      synchronized (stats)
      {
          copy = new HashMap&lt;String, Long&gt;(stats);
          stats.clear();
      }
      return copy;
  }
  }
  </code></pre>
  
  <p>this is my second class, a harvester which collects the stats from time to time and writes them to a database.</p>
  
  <pre><code>public class StatsHarvester implements Runnable
  {
  private StatsService    statsService;
  private Thread          t;
  
  public void init ( )
  {
      t = new Thread(this);
      t.start();
  }
  
  public synchronized void run ( )
  {
      while (true)
      {
          try
          {
              wait(5 * 60 * 1000); // 5 minutes
              collectAndSave();
          }
          catch (InterruptedException e)
          {
              e.printStackTrace();
          }
      }
  }
  
  private void collectAndSave ( )
  {
      Map&lt;String, Long&gt; stats = statsService.getStats();
      // do something like:
      // saveRecords(stats);
  }
  }
  </code></pre>
  
  <p>At runtime it will have about 30 concurrent running threads each calling <code>notify(key)</code> about 100 times. Only one StatsHarvester is calling <code>statsService.getStats()</code></p>
  
  <p>So i have many writers and only one reader. it would be nice to have accurate stats but i don't care if some records are lost on high concurrency.</p>
  
  <p>The reader should run every 5 Minutes or whatever is reasonable.</p>
  
  <p>Writing should be as fast as possible. Reading should be fast but if it locks for about 300ms every 5 minutes, its fine.  </p>
  
  <p>I've read many docs (Java concurrency in practice, effective java and so on), but i have the strong feeling that i need your advice to get it right.</p>
  
  <p>I hope i stated my problem clear and short enough to get valuable help.</p>
  
  <hr>
  
  <h2>EDIT</h2>
  
  <p>Thanks to all for your detailed and helpful answers. As i expected there is more than one way to do it.</p>
  
  <p>I tested most of your proposals (those i understood) and uploaded a test project to google code for further reference (maven project)</p>
  
  <p><a href="http://code.google.com/p/javastats/" rel="noreferrer">http://code.google.com/p/javastats/</a></p>
  
  <p>I have tested different implementations of my StatsService</p>
  
  <ul>
  <li>HashMapStatsService (HMSS)</li>
  <li>ConcurrentHashMapStatsService (CHMSS)</li>
  <li>LinkedQueueStatsService (LQSS)</li>
  <li>GoogleStatsService (GSS)</li>
  <li>ExecutorConcurrentHashMapStatsService (ECHMSS)</li>
  <li>ExecutorHashMapStatsService (EHMSS)</li>
  </ul>
  
  <p>and i tested them with <code>x</code> number of Threads each calling notify <code>y</code> times, results are in ms</p>
  
  <pre><code>         10,100   10,1000  10,5000  50,100   50,1000  50,5000  100,100  100,1000 100,5000 
  GSS       1        5        17       7        21       117      7        37       254       Summe: 466
  ECHMSS    1        6        21       5        32       132      8        54       249       Summe: 508
  HMSS      1        8        45       8        52       233      11       103      449       Summe: 910
  EHMSS     1        5        24       7        31       113      8        67       235       Summe: 491
  CHMSS     1        2        9        3        11       40       7        26       72        Summe: 171
  LQSS      0        3        11       3        16       56       6        27       144       Summe: 266
  </code></pre>
  
  <p>At this moment i think i will use ConcurrentHashMap, as it offers good performance while it is quite easy to understand.</p>
  
  <p>Thanks for all your input!
  Janning</p>