0

I have an application with a single writer thread which does some stuff and updates some metrics e.g. counters etc. The application has a number of other threads which read the stats and do stuff with them. It's not essential that the metrics are up to date but the writer thread needs to be blocked for as little time as possible. I was wondering which of the following would better suit my needs:

Option 1 - have a non atomic field that only the writer can update, and an AtomicXXX field which is set using lazySet:

class Stats1
{
  private final AtomicLong someCounterAtomic = new AtomicLong(0);
  private long someCounter = 0;

  // writer thread updates the stats using this
  public void incrementCounter(long counterIncrement)
  {
    someCounter += counterIncrement;
    someCounterAtomic.lazySet(someCounter);
  }

  // reader threads call this
  public long getCounterValue()
  {
    return someCounterAtomic.get();
  }
}

Option 2 - Just have an AtomicXXX field which is updated via addAndGet:

class Stats2
{
  private final AtomicLong someCounter = new AtomicLong(0);

  // writer thread updates the stats using this
  public void incrementCounter(long counterIncrement)
  {
    someCounter.addAndGet(counterIncrement);
  }

  // reader threads call this
  public long getCounterValue()
  {
    return someCounter.get();
  }
}

Or would I be better off doing something else?

Thanks in advance.

PaddyD
  • 1,097
  • 1
  • 9
  • 19

4 Answers4

3

Just use addAndGet. It's simpler, instantly recognizable, and very probably fast enough.

If there's only one thread updating the counter, then addAndGet is uncontested and will be about as fast as your Stats1 code. If there's multiple threads updating, then Stats1 is just plain wrong. Either way, you shouldn't complicate the code unless you have benchmarking/profiling data that suggests that it's a significant cost to your app's overall run time.

yshavit
  • 42,327
  • 7
  • 87
  • 124
1

In case somewhat recent stats are fine, I wouldn't synchronize anything. I would have the writer thread take periodical snapshots and have them published periodically (or after the stats have changed enough) through an atomic reference.

public class Stats {
     // the currently available stat instance
     public static AtomicReference<Stats> CURRENT_STATS = 
         new AtomicReference<>(new Stats());

     private final int whatever1;
     private final int whatever2;

     private Stats() {
         this(0, 0);
     }

     public Stats(int whatever1, int whatever2) {
         this.whatever1 = whatever1;
         ...
     }

     public int getWhatever1() {
         return whatever1;
     }

     public int getWhatever2() {
         return whatever2;
     }

}

The writer just creates a new Stats instance when it sees fit and sets the CURRENT_STATS reference. Readers simply read the reference when they need the stats and keep hold of the reference until they're done with the current processing pass (avoids the stats changing while processing).

This requires minimal synchronization, suitable when the requirements for the stats being up-to date is lenient (e.g. for displaying throughput etc. to the user). Regardless of how many variables Stats exposes, a single atomic reference can control all of them.

Durandal
  • 19,919
  • 4
  • 36
  • 70
  • I have accepted this answer because I need to keep the buffering/latency of the data being processed by the thread writing the stats to a minimum. Thanks for the suggestion. – PaddyD Aug 19 '14 at 21:39
1

If you are using Java 8, you should consider the LongAdder class in java.util.concurrent.atomic:

This class is usually preferable to AtomicLong when multiple threads update a common sum that is used for purposes such as collecting statistics, not for fine-grained synchronization control. Under low update contention, the two classes have similar characteristics. But under high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.

Note that if you follow the suggestion in the accepted answer of never synchronizing, then it's possible that the statistics will never update (depending on what else is going on in your program, which architecture you're running on, which Java version, etc.).

Martin Ellis
  • 9,603
  • 42
  • 53
  • Unfortunately, for various reasons, I'm unable to use Java 8 for this application. That class looks like it would be suitable though. – PaddyD Aug 19 '14 at 21:31
0

In single thread environment you can use lazySet . getAndSet is mainly for multi thread enviroment

Siva Kumar
  • 1,983
  • 3
  • 14
  • 26
  • but is incrementing a non-atomic value and using that to set an atomic value using lazySet faster than using addAndGet? – PaddyD Aug 19 '14 at 17:03
  • @PaddyD yes . Performance wise better performance in case if you are using single thread. – Siva Kumar Aug 19 '14 at 17:05