AtomicLong: LongAdder: LongAdder: LongAdder: LongAdder: LongAdder: LongAdder: LongAdder: LongAdder This article explains the difference between Adder and Accumulator.
Both Adder and Accumulator were introduced in Java 8 and are relatively new classes. For Adder, for example, the most typical of LongAdder, as described in the previous article, LongAdder is more efficient than AtomicLong at high concurrency. Because AtomicLong is only suitable for low concurrency scenarios, otherwise in high concurrency scenarios, due to the high conflict probability of CAS, it will lead to frequent spin, affecting the overall efficiency.
When the competition is not fierce, all threads modify the same Base variable through CAS. However, when the competition is fierce, LongAdder will modify different threads on different cells to reduce the probability of conflict. This improves concurrency.
Accumulator
So what is Accumulator? Accumulator is very similar to Adder. In fact, Accumulator is a more general version of Adder. For example, Longator is an enhanced version of LongAdder. Because LongAdder’s API only adds and subtracts values, LongAccumulator provides custom function operations.
To illustrate this with a code example, we’ve all heard the story of mathematician Gauss. When gauss was young, his teacher gave him a math problem, starting from 1,2… What are the numbers in 100 that you add up to. For those of you who know the story, gauss’s algorithm is to take 1+ 99,2 +98… And that’s the result. Here is the simulation of this question. Through the method of LongAccumulator custom function, the direct result codes of calculation from 1 to 10 are as follows
public class LongAccumulatorDemo {
public static void main(String[] args) throws InterruptedException {
LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0);
ExecutorService executor = Executors.newFixedThreadPool(8);
IntStream.range(1.11).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));
Thread.sleep(2000); System.out.println(accumulator.getThenReset()); }}Copy the code
In this code:
- First, create a new LongAccumulator and pass it two parameters.
- Then create a new pool of 8 threads and submit 10 tasks from 1 to 10 to the pool using an IntStream.
- It then waits two seconds for the task to complete in the thread pool.
- Finally, print out the value of Accumulator.
The result of this code is 55. Of course, the computer does not calculate the demerit according to Gauss’s idea. The calculation process here is still a number of cumulative. On behalf of 0 + 1 + 2 + 3 +… Plus 8 plus 9 plus 10 is equal to 55. LongAccumulator = LongAccumulator = LongAccumulator
LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0);
Copy the code
In this statement, two arguments are passed: the first argument to the LongAccumulator constructor is a binary expression; The second argument is the initial value of x, passed in as 0. In binary expressions, x is the result of the last evaluation (except for the first one), and y is the new value passed in this time.
Accumulator.accumulate (1) : x is the second argument to the LongAccumulator constructor. The initial y value is the 1 passed to accumulator.accumulate(1). Accumulator.accumulate (2) is the result of an accumulator.accumulate(2). The result is 1+2=3.
ForEach (I -> executor.submit(() -> accumulator.accumulate(I))); This statement actually uses the integer stream, and commits 10 tasks from 1 to 10 to the thread pool.
accumulator.accumulate(1);
accumulator.accumulate(2);
accumulator.accumulate(3); . accumulator.accumulate(8);
accumulator.accumulate(9);
accumulator.accumulate(10);
Copy the code
It should be pointed out that the order of addition is not fixed. It does not mean that the sum will start from 1 in ascending order. It may also change, such as adding 5 first, then 3, then 6. But anyway, because of the commutative law of addition, you’re guaranteed to end up with 55. This is one of the basic uses of this class.
expand
Let’s continue to see how powerful it is. For example, the expression we just gave was x + y, but we could also pass in x * y, or write math.min (x, y), which is the same thing as minimizing x and y. Similarly, math.max (x, y) is equivalent to finding a maximum value. Choose according to the needs of the business. The code is as follows:
LongAccumulator counter = new LongAccumulator((x, y) -> x + y, 0);
LongAccumulator result = new LongAccumulator((x, y) -> x * y, 0);
LongAccumulator min = new LongAccumulator((x, y) -> Math.min(x, y), 0);
LongAccumulator max = new LongAccumulator((x, y) -> Math.max(x, y), 0);
Copy the code
At this point you might be wondering: Why not use the for loop here? For example, in our previous example, from 0 to 10, couldn’t we just write a for loop?
Sure, a for loop would do the job, but with a for loop, it would execute sequentially, and it would be 0+1+2+3+… The order of +8+9+10 is added, but LongAccumulator has the advantage of using the thread pool to work for it. Once a thread pool is used, multiple threads can be computed in parallel, much more efficiently than the previous serialization. That’s why the order in which it is added is not fixed, because we can’t guarantee the order of execution between threads, all we can guarantee is that the end result is certain.
Usage scenarios
The first condition that needs to be met is that a large amount of computation is required, and LongAccumulator can be considered when parallel computation is required.
You can use the for loop when the computation is small, or when serial computation is sufficient. If the amount of calculation is large and the efficiency of calculation needs to be improved, we can use the thread pool and LongAccumulator to achieve the effect of parallel calculation with high efficiency.
The second requirement is that the order of execution does not matter, that is, it does not require the order of execution between computations, that is, thread 1 May be executed after thread 5, or before thread 5, but the order of execution does not affect the final result.
Some very typical computations that satisfy this condition are things like addition or multiplication, because they have commutative laws. Similarly, there is no order requirement for the maximum and minimum values, because only the maximum or minimum of all numbers will be obtained, and whichever is submitted first or later will not affect the final result.