什么是原子类

  • 不可分割
  • 一个操作是不可中断的,即便是多线程的情况下也是可以保证的
  • java.util.concurrent.atomic包下
  • 原子类的作用和锁类似,是为了保证并发情况下线程安全,原子类相比于锁,有一定的优势
  • 粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得最细粒度的情况了
  • 性能更高:通常情况下使用原子类的效率比使用锁的效率更高,除了高度竞争的情况
Atomic*基本原子类

AtomicInteger为例

  • public final int get():获取当前值
  • public final int getAndSet(int newValue):获取当前值然后设置新值
  • public final int getAndIncrement():获取当前值然后自增
  • public final int getAndDecrement():获取当前值然后自减
  • public final int getAndAdd(int delta):获取当前值然后加上参数中的值
  • public final int compareAndSet(int expect,int update):如果当前数值等于预期值,则以原子方式将该值设置进去

原子类和非原子类对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private static final AtomicInteger atomicInteger = new AtomicInteger();

public void incrementAtomic() {
atomicInteger.getAndIncrement();
}

private static volatile int basicCount = 0;

public synchronized void incrementBasic() {
basicCount++;
}

@Override
public void run() {
for (int i = 0; i < 10000; i++) {
incrementAtomic();
incrementBasic();
}
}

public static void main(String[] args) throws InterruptedException {
AtomicIntergerDemo atomicIntergerDemo = new AtomicIntergerDemo();
Thread thread1 = new Thread(atomicIntergerDemo);
Thread thread2 = new Thread(atomicIntergerDemo);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("原子类->"+atomicInteger.get());
System.out.println("非原子类->"+basicCount);

}

如果非原子类不加锁就不能保证正确的结果

AtomicArray基本原子类

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class AtomicArrayDemo {

public static void main(String[] args) throws InterruptedException {
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000);

Thread[] threadsIncrementer = new Thread[100];
Thread[] threadsDecrementer = new Thread[100];

Incrementer incrementer = new Incrementer(atomicIntegerArray);
Decrementer decrementer = new Decrementer(atomicIntegerArray);

for (int i = 0; i < 100; i++) {
threadsDecrementer[i] = new Thread(decrementer);
threadsIncrementer[i] = new Thread(incrementer);
threadsDecrementer[i].start();
threadsIncrementer[i].start();
}

for (int i = 0; i < 100; i++) {
threadsDecrementer[i].join();
threadsIncrementer[i].join();
}

for (int i = 0; i < atomicIntegerArray.length(); i++) {
if (atomicIntegerArray.get(i)!= 0) {
System.out.println("发现了错误-" + i);
}
}
System.out.println("运行结束");
}
}

class Decrementer implements Runnable {

private AtomicIntegerArray array;

public Decrementer(AtomicIntegerArray array) {
this.array = array;
}

@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
array.getAndDecrement(i);
}
}
}

class Incrementer implements Runnable {

private AtomicIntegerArray array;

public Incrementer(AtomicIntegerArray array) {
this.array = array;
}

@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
array.getAndIncrement(i);
}
}
}
Atomic*Reference引用类型原子类

和AtomicInteger没有本质区别,AtomicReference可以让一个对象保证原子性,用法和AtomicInteger类似

把普通变量升级为具有原子功能
  • AtomicIntegerFieldUpdater对普通变量进行升级
  • 使用场景:偶尔需要一个原子get-set操作

升级后与普通变量的对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class AtomicIntegerFieldUpdaterDemo implements Runnable {

static Candidate aa;
static Candidate bb;

public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");

@Override
public void run() {
for (int i = 0; i < 10000; i++) {
aa.score++;
scoreUpdater.getAndIncrement(bb);
}
}

public static class Candidate {
volatile int score;
}

public static void main(String[] args) throws InterruptedException {
aa = new Candidate();
bb = new Candidate();

AtomicIntegerFieldUpdaterDemo updaterDemo = new AtomicIntegerFieldUpdaterDemo();

Thread t1 = new Thread(updaterDemo);
Thread t2 = new Thread(updaterDemo);

t1.start();
t2.start();

t1.join();
t2.join();
System.out.println("普通->" + aa.score);
System.out.println("升级的->" + bb.score);
}
}

注意点:

  • 必须要是可见的,private是不行的
  • 不支持static
Adder累加器
  • java8引入的,相对较新
  • 高并发下LongAdder比AtomicLong效率高,本质是空间换时间
  • 竞争激烈的时候,LongAdder把不同线程对应到不用的Cell上进行修改,降低了冲突的概率,是多段锁的概念,提高了并发性

AtomicLong代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class AtomicLongDemo {

public static void main(String[] args) throws InterruptedException {
AtomicLong counter = new AtomicLong(0);

ExecutorService service = Executors.newFixedThreadPool(20);

long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
service.submit(new Task(counter));
}
service.shutdown();

while (!service.isTerminated()) {

}
long end = System.currentTimeMillis();

System.out.println(counter.get());
System.out.println("耗时->"+(end-start));
}

private static class Task implements Runnable {

private AtomicLong counter;

public Task(AtomicLong counter) {
this.counter = counter;
}

@Override
public void run() {
for (int i = 0; i < 10000; i++) {
counter.getAndIncrement();
}
}
}
}

LongAdder代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class LongAdderDemo {

public static void main(String[] args) throws InterruptedException {
LongAdder counter = new LongAdder();

ExecutorService service = Executors.newFixedThreadPool(20);

long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
service.submit(new Task(counter));
}
service.shutdown();

while (!service.isTerminated()) {

}
long end = System.currentTimeMillis();

System.out.println(counter.sum());
System.out.println("耗时->"+(end-start));
}

private static class Task implements Runnable {

private LongAdder counter;

public Task(LongAdder counter) {
this.counter = counter;
}

@Override
public void run() {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
}
}
}

最后根据打印结果可以看出LongAdder耗时比AtomicLong少得多

LongAdder带来的改进和原理
  • AtomicLong的每一次加法都需要做同步,所以在高并发的时候导致冲突比较多,降低了效率
  • LongAdder每一个线程会有自己的一个计数器,仅用来在自己线程计数,就不会和其他线程计数器有干扰
  • LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数
    • base变量:竞争不激烈,直接累加到该变量上
    • cell[]数组:竞争激烈,各个线程分散累加到自己的Cell[i]中
  • sum就是把base和每一个cell加起来
对比
  • 在低争用的情况下,两个类具有相似的特征,但竞争激烈的情况下,LongAdder的吞吐量要高得多,但消耗更多空间
  • LongAdder只有add方法,所以只适合统计求和计数,而AtomicLong还具有cas方法