Regs
Regs

Reputation: 351

A thread safe eventually consistent counter

I have a highly concurrent code that should be able to increment \ decrement a few counters concurrently and read the values. I don't require exact values at every read, so it might as well be eventually consistent. My main objective is that the write operations are non blocking and require no wait time, basically a few threads want to increment the same counter, call some increment function and don't wait for it to be processed. However, I'm having hard figuring out a way to make such a counter.

I was thinking about using ConcurrentLinkedQueue<Boolean>. Add an element to the queue, and have another thread to pop elements and count increments \ decrements. However, it's not a BlockingQueue so I'd have to make a thread that constantly tries to poll the queue, feels like a huge waste to have a thread fully dedicated to this task. Just asking for size() is not an option because ConcurrentLinkedQueue the operation isn't constant time and every call has to traverse the entire queue, that would be insane waste of time.

I also looked at AtomicInteger but there is only lazy set operation, no lazy increments, incrementAndGet if I understand correctly would result in a locking based increment-read behavior which is definitely not what I need.

Is using a ConcurrentLinkedQueue<Boolean> and a dedicated polling thread my only option for an eventually consistent counter? Especially considering that I do not know how many threads will be trying to write and read this counter at any moment of time, these are spawned dynamically.

Upvotes: 3

Views: 796

Answers (2)

dnault
dnault

Reputation: 8909

Sounds like java.util.concurrent.LongAdder might be what you're looking for:

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.

Upvotes: 5

slesh
slesh

Reputation: 2017

My main objective is that the write operations are non-blocking and require no wait time

It makes me think that like you need to introduce kind of async write.

I'd have to make a thread that constantly tries to poll the queue

You may try to create kind of increment-task using CompletableFuture which uses ForkJoinPool (or created dedicated single thread pool) with one thread which will eventually apply all write operations to some AtomicLong so the worker threads will not be blocked:

public class AsyncCounter {
    private final AtomicLong counter = new AtomicLong();

    // no-blocking here
    public CompletableFuture<Long> inc() {
        return CompletableFuture.supplyAsync(counter::incrementAndGet);
    }

    public long get() {
        return counter.get();
    }
}

But in any case, it should be tested using JMH because it usually happens that our consideration regarding performance is wrong.

Upvotes: -2

Related Questions