> From: "Dain Ironfoot" <[email protected]>
> To: "mechanical-sympathy" <[email protected]>
> Sent: Saturday, June 4, 2022 5:11:49 PM
> Subject: Re: Compute in a lock free way

> Thank you so much Remi!!
> 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?

I think you can separate the concurrency part from the algorithm part and 
always have the reference initialized to avoid those pesky nulls, like this 

public final class Computer { 
private static final class Stat { 
private final BigDecimal sum; 
private final int count; 

public Stat(BigDecimal sum, int count) { 
this.sum = sum; 
this.count = count; 
} 

public Stat add(double value) { 
return new Stat(sum.add(BigDecimal.valueOf(value)), count + 1); 
} 

public double compute() { 
return sum.divide(BigDecimal.valueOf(count)).doubleValue(); 
} 
} 

private final AtomicReference<Stat> statReference; 

public Computer() { 
this.statReference = new AtomicReference<>(new Stat(BigDecimal.ZERO, 0)); 
} 

public void add(double value) { 
statReference.getAndUpdate(s -> s.add(value)); 
} 

public double compute() { 
return statReference.get().compute(); 
} 
} 

> 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" < [ https://partage.u-pem.fr/mail | 
>>> [email protected] ] >
>>> To: "mechanical-sympathy" < [ https://partage.u-pem.fr/mail |
>>> [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
>>  |
>> 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 [ https://partage.u-pem.fr/mail | [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?utm_medium=email&utm_source=footer
>>> |
>>> https://groups.google.com/d/msgid/mechanical-sympathy/2d083587-2ee1-4f8f-a7ee-a99062a6ef5cn%40googlegroups.com
>>> ] .

> --
> 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 [ mailto:[email protected] |
> [email protected] ] .
> To view this discussion on the web, visit [
> https://groups.google.com/d/msgid/mechanical-sympathy/e7baf028-30ac-4004-bc1c-55c7649e3ebdn%40googlegroups.com?utm_medium=email&utm_source=footer
> |
> https://groups.google.com/d/msgid/mechanical-sympathy/e7baf028-30ac-4004-bc1c-55c7649e3ebdn%40googlegroups.com
> ] .

-- 
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/1661909019.2141987.1654367251403.JavaMail.zimbra%40u-pem.fr.

Reply via email to