0

Let's say I have the following class in Java:

class Record {
  
  String name;
  double count;
  long repeat;
  
  public Record(String name){
    this.name = name;
  }

  public synchronized void update(Record other){
    this.count = (other.count * other.repeat + this.count * this.repeat)/(other.repeat + this.repeat);
    this.repeat = this.repeat + other.repeat;
  }

Now I have a map of such records ConcurrentHashMap<String, Record> recordConcurrentHashMap;

and I want to create a thread-safe correct update function.

Currently I have done this:

static ConcurrentHashMap<String,Record> recordConcurrentHashMap;

public static void updateRecords(Record other){
    Record record = recordConcurrentHashMap.computeIfAbsent(other.name, Record::new);
    record.update(other);
}

I am having to keep the update function synchronized to achieve correctness.

Can I do this without synchronized using LongAdder or LongAccumulator?

I tried using those, but couldn't figure out how to achieve the complex calculation with them.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
Adwait Kumar
  • 1,552
  • 10
  • 25

1 Answers1

3

No, you can't, certainly not with those.

What you might consider doing -- which would avoid the synchronized -- would be to make Record immutable and unmodifiable, and do something like

class Record {
  final String name;
  final double count;
  final long repeat;

  public Record(String name){
    this.name = name;
  }

  private Record(String name, double count, long repeat) {
    this.name = name; this.count = count; this.repeat = repeat;
  }

  public Record combine(Record other){
    return new Record(
      name,
      other.count * other.repeat + this.count * this.repeat)
         /(other.repeat + this.repeat),
      repeat + other.repeat);
  }
}

public static void updateRecords(Record other){
  Record record = recordConcurrentHashMap.merge(
    other.name, other, Record::combine);
}
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • Does it have to immutable, since merge is atomic? Can't merge call combine and return one of the Record's updated to occupy the map value? Can't the other Record be reused? – David G. Pickett Aug 28 '20 at 00:29
  • Yes, it does need to be immutable; otherwise multiple merges on the same record could race and even cause nonsensical output -- in the absence of the lock, which the point was to eliminate. (Note, for example, that `merge` may call the method multiple times in the event of contention, so you might end up combining the values twice!) – Louis Wasserman Aug 28 '20 at 01:10