Thank you so much!!
I took your advice of using CAS to update both of them and came up with
this (Can't use record as still on JDK 11).
Does this look ok?
public final class Computer {
public static class Stat{
private final BigDecimal sum;
private final int count;
public Stat(BigDecimal sum, int count){
this.sum = sum;
this.count = count;
}
}
private final AtomicReference<Stat> statReference;
public Computer ( ){
this.statReference = new AtomicReference<>();
}
public final void add ( int value ){
Stat oldStat = null;
Stat newStat = null;
do{
oldStat = statReference.get();
BigDecimal newSum = BigDecimal.valueOf(value);
BigDecimal newStatCheck = (oldStat == null ) ? newSum :
oldStat.sum.add(newSum);
int newCount = (oldStat == null ) ? 1 : oldStat.count + 1;
newStat = new Stat( newStatCheck, newCount);
}while( !statReference.compareAndSet(oldStat, newStat) );
}
public final double compute ( ){
Stat stat = statReference.get();
if( stat == null ){
return 0;
}else {
return stat.sum.divide(BigDecimal.valueOf(stat.count)).doubleValue();
}
}
}
On Saturday, June 4, 2022 at 3:45:50 AM UTC-5 [email protected] wrote:
> *From: *"Dain Ironfoot" <[email protected]>
> *To: *"mechanical-sympathy" <[email protected]>
> *Sent: *Saturday, June 4, 2022 12:53:55 AM
> *Subject: *Compute in a lock free way
>
> Hello,
> I am writing a class to calculate average of prices. We use it to generate
> buy/sell signal for some financial instruments therefore latency is
> crucial. Prices are sent via different threads therefore class needs to be
> thread-safe.
>
> Am I correct in understanding that I have to use a lock to make the two
> operations (adding and incrementing) atomic? I have looked at
> AtomicLong/LongAdder/LongAccumulator but looks like they can only sum the
> numbers atomically.
>
> *In other words, there is no way to do this in a lock-free manner? *
>
>
> you can group them in an object and do a CAS (see VarHandle.compareAndSet)
> on the references
>
> record Stat(BigDecimal sum, int count) { }
>
> public final class Computer {
> private volatile Stat stat;
>
> public void add(double value) {
> // do a CAS between stat and new Stat(BigDecimal.valueOf(value),
> stat.count + 1)
> }
> }
>
> BTW in your example, rwLock, readLock and writeLock should be declared
> final to avoid any publication issues,
> see
> https://stackoverflow.com/questions/1621435/not-thread-safe-object-publishing
>
> regards,
> Rémi
>
>
>
> Thank you!
>
>
>
> public final class Computer{
>
> private ReentrantReadWriteLock rwLock;
> private ReadLock readLock;
> private WriteLock writeLock;
>
> private BigDecimal sum;
> private int count;
>
> public AverageCalculatorThreadSafeImplementation2( ){
> this.rwLock = new ReentrantReadWriteLock();
> this.readLock = rwLock.readLock();
> this.writeLock = rwLock.writeLock();
> this.sum = new BigDecimal(0);
> }
>
> public final void add( double value ){
> writeLock.lock();
> try{
> sum = sum.add( BigDecimal.*valueOf*(value) );
> ++count;
> }finally{
> writeLock.unlock();
> }
> }
>
> public final double compute( ){
> readLock.lock();
> try{
> return sum.divide(BigDecimal.valueOf(count)).doubleValue();
> }finally{
> readLock.unlock();
> }
> }
>
> --
> You received this message because you are subscribed to the Google Groups
> "mechanical-sympathy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/mechanical-sympathy/2d083587-2ee1-4f8f-a7ee-a99062a6ef5cn%40googlegroups.com
>
> <https://groups.google.com/d/msgid/mechanical-sympathy/2d083587-2ee1-4f8f-a7ee-a99062a6ef5cn%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
>
--
You received this message because you are subscribed to the Google Groups
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web, visit
https://groups.google.com/d/msgid/mechanical-sympathy/9c2c97a1-c9ca-4151-bbca-519087d6da54n%40googlegroups.com.