Hello everyone, I’m
方圆
here. Let’s mainly write about the things that are elevated under the Atomic package
table of Contents
1. Recognize and recognize atomic classes
The role of the atomic class is similar to the lock, it is to ensure concurrency 线程安全
, and its 粒度更细
,效率更高
2. Overview of Atomic Classes
3. AtomicIntegerFieldUpdater
This class can be used when we need to perform atomic operations on a field 升级
, so that it saves space and optimizes performance.
3.1 Code demonstration
Note that it should be modified with volatile, but cannot be modified with static
public class AtomicIntegerFieldUpdateDemo implements Runnable{
//TODO 注意字段需要被volatile修饰,但是不能是static修饰的
private volatile int a = 0;
private volatile int b = 0;
//用法有点儿像反射
private static final AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdateDemo> updater =
AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdateDemo.class,"a");
public int getA() {
return a;
}
public int getB() {
return b;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
updater.getAndIncrement(this);
b++;
}
}
}
class Test {
public static void main(String[] args) throws InterruptedException {
AtomicIntegerFieldUpdateDemo demo = new AtomicIntegerFieldUpdateDemo();
new Thread(demo).start();
new Thread(demo).start();
TimeUnit.SECONDS.sleep(1);
System.out.println("升级过的int " + demo.getA());
System.out.println("普通的int " + demo.getB());
}
}
- Test Results
4. Adder accumulator
- It was added in JDK1.8
- Compared with AtomicLong under high concurrency, LongAdder is
效率更高
essentially used空间换时间
(we will take a look at the source code later) - LongAdder puts different threads corresponding to different Cells, and it maintains one at the bottom
Cell数组
, which is equivalent to the concept of multi-segment locks, which improves the performance of concurrency
4.1 LongAdder code test
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAdder;
public class LongAdderDemo implements Runnable{
private LongAdder longAdder;
public LongAdderDemo(LongAdder longAdder) {
this.longAdder = longAdder;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
longAdder.increment();
}
}
}
class TestLongAdder {
public static void main(String[] args) {
LongAdder adder = new LongAdder();
ExecutorService threadPool = Executors.newFixedThreadPool(8);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
threadPool.submit(new LongAdderDemo(adder));
}
threadPool.shutdown();
while(! threadPool.isTerminated()) {
}
long endTime = System.currentTimeMillis();
System.out.println(adder.sum());
System.out.println("耗时 " + (endTime - startTime) + "ms");
}
}
- result:
4.2 AtomicLong code test
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
public class AtomicLongDemo implements Runnable{
private AtomicLong atomicLong;
public AtomicLongDemo(AtomicLong atomicLong) {
this.atomicLong = atomicLong;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
atomicLong.incrementAndGet();
}
}
}
class TestAtomicDemo {
public static void main(String[] args) {
AtomicLong atomicLong = new AtomicLong();
ExecutorService threadPool = Executors.newFixedThreadPool(8);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
threadPool.submit(new AtomicLongDemo(atomicLong));
}
threadPool.shutdown();
while (! threadPool.isTerminated()) {
}
long endTime = System.currentTimeMillis();
System.out.println(atomicLong.get());
System.out.println("耗时 " + (endTime - startTime) + "ms");
}
}
- result
4.3 Summary
We can find that under high concurrency, LongAdder has better performance, mainly when AtomicLong is in 每一次加法
place flush和refresh
(in JMM, synchronize back and forth between its own working memory and main memory), resulting in a waste of resources
The implementation of LongAdder is originally different from AtomicLong. It does not need to be synchronized, but each thread has its own counter, and no thread will unify the count.
There are two important variables, Cell数组
and the base基值
former is high. Used in concurrency, each thread records its own accumulated value. The latter is used when the competition is not fierce, and it is directly added to the base variable.
We read sum方法
the source code
In the case of low concurrency, AtomicLong and LongAdder have almost the same performance, but when the concurrency increases, the throughput of LongAdder is large, but it consumes more space, essentially using space for time; on the other hand, LongAdder No CAS method is provided. This is its shortcoming compared to AtomicLong. LongAdder is more suitable for statistical summation and counting scenarios.
5. Accumulator
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.stream.IntStream;
public class AccumulatorDemo {
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator((x,y) -> 2 * x + y,1);
ExecutorService threadPool = Executors.newFixedThreadPool(8);
IntStream.range(1,3).forEach(i -> threadPool.submit(() -> accumulator.accumulate(i)));
threadPool.shutdown();
while(! threadPool.isTerminated()) {
}
System.out.println(accumulator.getThenReset());
}
}
It is equivalent to an upgrade to LongAdder, we can specify the calculation formula, its bottom layer also maintains the Cell array and base base value, and can perform multi-threaded calculations
Come on! ! !