[concurrency-interest] LongAdder with custom behavior?

Martin Buchholz martinrb at google.com
Fri Dec 1 14:18:06 EST 2017


It seems straightforward to use VarHandle getAndSet methods in LongAdder
(and Striped64) and should be strictly more efficient.   I would
write sumThenReset like this:

    /**
     * Equivalent in effect to {@link #sum} followed by {@link #reset}.
     * This method may apply for example during quiescent points
     * between multithreaded computations.  If there are updates
     * concurrent with this method, the returned value is <em>not</em>
     * guaranteed to be the final value occurring before the reset.
     *
     * @return the sum
     */
    public long sumThenReset() {
        final Cell[] cells = this.cells;
        long sum = getAndSetBase(0L);
        if (cells != null)
            for (Cell cell : cells)
                if (cell != null)
                    sum += cell.getAndSet(0L);
        return sum;
    }

With the use of getAndSet, we can then consider using sumThenReset for
concurrency control.  A consumer can "harvest" counts that have collected
in the LongAdder in Semaphore style, without losing any concurrent counts.
The hard part is writing the spec!  Unfortunately the name sumThenReset
becomes a bit of a misnomer as it strongly implies non-thread-safety and
use of reset().


On Thu, Nov 30, 2017 at 11:18 PM, Carl Mastrangelo via Concurrency-interest
<concurrency-interest at cs.oswego.edu> wrote:

> While looking at LongAdder as a possible candidate for a concurrent
> counter, I noticed three things that made it seem surprising.  I am
> wondering what the rationale is for these since they don't make sense to me:
>
> 1.  LongAdder.sumThenReset seems to not be thread-safe.  If concurrent
> writes happen between reading and resetting of each Cell, then the write
> could get dropped.    This matches the behavior described in reset(), but
> makes the class less useful.   For instance, I would like sumThenReset to
> tally up all the mutations, and reset the counter back to zero without
> dropping mutations.  This would make it so I call sumThenReset later, and
> pick up any mutations I missed.
>
> 2.  Following up on the first point, the implementation of sumThenReset
> does two volatile operations on each Cell.value.  First it reads, followed
> by setting it to zero.  Why doesn't it use getAndSet on the value?  On the
> surface it would appear to be fewer synchronization points.   Also, it
> would nicely solve the first item.
>
> 3.   In the case that the LongAdder only ever increases in value (no
> resets), it would be possible to provide a means to see if it's equal to
> zero.  I believe this would be cheaper to implement than summing across all
> Cells.  The list of cells could be walked until there was a single nonzero
> cell, at which point it could return early.  This would be similar to the
> isEmpty() method on ConcurrentLinkedQueue, which doesn't need to walk the
> whole list.
>
> The reason I bring this up is that although the LongAdder class is
> non-final, there is no way to provide an isEmpty() myself.  All the access
> to the Cells and value are package private.  If I did implement this, I
> would have to give up the nice contention features.
>
> Is LongAdder reusable or should I try making my own counter?  Also, were
> the issues I bring up now brought up during the design?
>
> Carl
>
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20171201/74e4df2f/attachment.html>


More information about the Concurrency-interest mailing list