> 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.