原子类
原子类
原子是不可分割的最小单位,故原子类可以认为其操作都是不可分割。
一个操作是不可中断的,即便是在多线程情况下也可以保证。
java.uti.concurrent.aomic
包下有很多具有原子特性的类。
原子类 VS 锁
- 粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况,锁的粒度通常大于原子类。
- 效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况下。
6 类原子类纵览
基本类型原子类
以 AtomicInteger 为例,本质是对 Integer 的封装,提供原子的访问和更新操作,其本质是基于 CAS 技术。
AtomicInteger 常用方法
1* get() 获取当前的值
2* getAndSet() 获取当前的值,并设置新的值
3* getAndIncrement() 获取当前的值,并自增
4* getAndDecrement() 获取当前的值,并自减
5* getAndAdd() 获取当前的值,并加上预期的值
6* compareAndSet() 如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
示例:原子类的用法
1import java.util.concurrent.atomic.AtomicInteger;
2
3/**
4 * 描述: 演示AtomicInteger的基本用法,对比非原子类的线程安全问题,使用了原子类之后,不需要加锁,也可以保证线程安全。
5 */
6public class AtomicIntegerDemo1 implements Runnable {
7
8 private static final AtomicInteger atomicInteger = new AtomicInteger();
9
10 private static volatile int basicCount = 0;
11
12 public void incrementAtomic() {
13 atomicInteger.getAndAdd(-90);
14 //atomicInteger.getAndIncrement();
15 }
16
17 public void incrementBasic() {
18 basicCount++;
19 }
20
21 public static void main(String[] args) throws InterruptedException {
22 AtomicIntegerDemo1 r = new AtomicIntegerDemo1();
23 Thread t1 = new Thread(r);
24 Thread t2 = new Thread(r);
25 t1.start();
26 t2.start();
27 t1.join();
28 t2.join();
29 System.out.println("原子类的结果:" + atomicInteger.get());
30 System.out.println("普通变量的结果:" + basicCount);
31 }
32
33 @Override
34 public void run() {
35 for (int i = 0; i < 10000; i++) {
36 incrementAtomic();
37 incrementBasic();
38 }
39 }
40}
1原子类的结果:-1800000
2普通变量的结果:19663
数组类型原子类
演示原子数组的使用方法
1import java.util.concurrent.atomic.AtomicIntegerArray;
2
3/**
4 * 描述: 演示原子数组的使用方法
5 */
6public class AtomicArrayDemo {
7
8 public static void main(String[] args) {
9 AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000);
10 Incrementer incrementer = new Incrementer(atomicIntegerArray);
11 Decrementer decrementer = new Decrementer(atomicIntegerArray);
12 Thread[] threadsIncrementer = new Thread[100];
13 Thread[] threadsDecrementer = new Thread[100];
14 for (int i = 0; i < 100; i++) {
15 threadsDecrementer[i] = new Thread(decrementer);
16 threadsIncrementer[i] = new Thread(incrementer);
17 threadsDecrementer[i].start();
18 threadsIncrementer[i].start();
19 }
20
21// Thread.sleep(10000);
22 for (int i = 0; i < 100; i++) {
23 try {
24 threadsDecrementer[i].join();
25 threadsIncrementer[i].join();
26 } catch (InterruptedException e) {
27 e.printStackTrace();
28 }
29 }
30
31 for (int i = 0; i < atomicIntegerArray.length(); i++) {
32 //开始是 0,最后也还是 0,找 0 印证是否执行成功
33// if (atomicIntegerArray.get(i)!=0) {
34// System.out.println("发现了错误"+i);
35// }
36 System.out.println(atomicIntegerArray.get(i));
37 }
38 System.out.println("运行结束");
39 }
40}
41
42class Decrementer implements Runnable {
43
44 private AtomicIntegerArray array;
45
46 public Decrementer(AtomicIntegerArray array) {
47 this.array = array;
48 }
49
50 @Override
51 public void run() {
52 for (int i = 0; i < array.length(); i++) {
53 array.getAndDecrement(i);
54 }
55 }
56}
57
58class Incrementer implements Runnable {
59
60 private AtomicIntegerArray array;
61
62 public Incrementer(AtomicIntegerArray array) {
63 this.array = array;
64 }
65
66 @Override
67 public void run() {
68 for (int i = 0; i < array.length(); i++) {
69 array.getAndIncrement(i);
70 }
71 }
72}
引用类型原子类
AtomicReference 可以让一个对象保证原子性,功能比基本类型原子类强一些,因为一个对象中可以包含多个属性。
示例:引用类可以实现自旋锁
1/**
2 * 描述: 自旋锁
3 */
4public class SpinLock {
5 //原子引用类(具备CAS能力)
6 private AtomicReference<Thread> sign = new AtomicReference<>();
7
8 /**
9 * 加锁
10 */
11 public void lock() {
12 Thread current = Thread.currentThread();
13 //期待没有人持有锁(null)、让当前线程持有锁(current)
14 //直到原子引用被赋值为当前的线程之后,才会停止。
15 while (!sign.compareAndSet(null, current)) {
16 try {
17 Thread.sleep(1);
18 } catch (InterruptedException e) {
19 e.printStackTrace();
20 }
21 System.out.println(Thread.currentThread().getName() + "自旋获取失败,再次尝试");
22 }
23 }
24
25 /**
26 * 解锁
27 */
28 public void unlock() {
29 Thread current = Thread.currentThread();
30 //期待持有锁的人是current线程,然后清除锁
31 sign.compareAndSet(current, null);
32 }
33
34 public static void main(String[] args) throws InterruptedException {
35 SpinLock spinLock = new SpinLock();
36 Runnable runnable = new Runnable() {
37 @Override
38 public void run() {
39 System.out.println(Thread.currentThread().getName() + "开始尝试获取自旋锁");
40 spinLock.lock();
41 System.out.println(Thread.currentThread().getName() + "获取到了自旋锁");
42 try {
43 Thread.sleep(300);
44 } catch (InterruptedException e) {
45 e.printStackTrace();
46 } finally {
47 spinLock.unlock();
48 System.out.println(Thread.currentThread().getName() + "释放了自旋锁");
49 }
50 }
51 };
52 Thread thread1 = new Thread(runnable);
53 Thread thread2 = new Thread(runnable);
54 thread1.start();
55 Thread.sleep(50);
56 thread2.start();
57 }
58}
把普通变量升级为具有原子性的变量
AtomicIntegerFieldUpdater 对普通变量进行升级
其背后原理利用的是反射
1* 可以范围(public)
2* 不支持 static
3*
适用场景如下:
- 变量类型不是由我们创建的,无权修改变量类型
- 偶尔需要一个原子 get-set 操作,并不是一直都需要原子操作。
1import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
2
3/**
4 * 描述: 演示AtomicIntegerFieldUpdater的用法
5 */
6public class AtomicIntegerFieldUpdaterDemo implements Runnable{
7
8 static Candidate tom;
9 static Candidate peter;
10
11 //原子类升级:需要引用类型和引用类型的具体操作的属性
12 public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
13 .newUpdater(Candidate.class, "score");
14
15 @Override
16 public void run() {
17 for (int i = 0; i < 10000; i++) {
18 peter.score++;
19 scoreUpdater.getAndIncrement(tom);//升级成原子类,操作对象中的属性自增
20 }
21 }
22
23 public static void main(String[] args) throws InterruptedException {
24 tom=new Candidate();
25 peter=new Candidate();
26 AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
27 Thread t1 = new Thread(r);
28 Thread t2 = new Thread(r);
29 t1.start();
30 t2.start();
31 t1.join();
32 t2.join();
33 System.out.println("普通变量:"+peter.score);
34 System.out.println("升级后的结果"+ tom.score);
35 }
36
37 public static class Candidate {
38
39 volatile int score;
40 }
41}
42
1普通变量:17517
2升级后的结果20000
Adder 累加器
累加器
是 Java 8 引入的,相对是比较新的一个类。
高并发下 LongAdder 比 AtomicLong 效率高(Integer 类型也一样),不过本质是空间换时间。
在竞争激烈的时候,LongAdder 把不同线程对应到不同的 Cell 上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性。
LongAdder 引入了分段累加的概念,内部有一个 base 变量和一个 Cell[] 数组共同参与计数:
1base变量:竞争不激烈,直接累加到该变量上。
2Cell[]数组:竞争激烈,各个线程分散累加到自己的槽 Cell[i] 中。
sum() 方法不是线程安全的,sum() = base + Cell[]
AtomicLong 和 LongAdder 使用场景
- 在低竞争的情况下,两者差不多,在高竞争的情况下,LongAdder 的预期吞吐量要高的多,但要消耗更多的内存(分段锁)。
- LongAdder 适合的场景是统计求和的计数的场景,并且 LongAdder 基本只提供了 add 方法。而 AtomicLong 还具有 cas 方法。
**AtomicLong **
AtomicLong 的实现原理是每一次加法都需要做同步,所以在并发的时候会导致冲突比较多,也就降低了效率。
示例:AtomicLong VS LongAdder
1* AtomicLong 的性能在多线程成竞争激烈的情况下,每一次加法,都要 flush 和 refresh,导致很耗费资源。
AtomicLongDemo
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3import java.util.concurrent.atomic.AtomicLong;
4
5/**
6 * 描述: 演示高并发场景下,LongAdder比AtomicLong性能好
7 */
8public class AtomicLongDemo {
9
10 public static void main(String[] args) throws InterruptedException {
11 AtomicLong counter = new AtomicLong(0);
12 ExecutorService service = Executors.newFixedThreadPool(20);
13 long start = System.currentTimeMillis();
14 for (int i = 0; i < 10000; i++) {
15 service.submit(new Task(counter));
16 }
17 service.shutdown();
18 while (!service.isTerminated()) {
19
20 }
21 long end = System.currentTimeMillis();
22 System.out.println(counter.get());
23 System.out.println("AtomicLong耗时:" + (end - start));
24 }
25
26 private static class Task implements Runnable {
27
28 private AtomicLong counter;
29
30 public Task(AtomicLong counter) {
31 this.counter = counter;
32 }
33
34 @Override
35 public void run() {
36 for (int i = 0; i < 10000; i++) {
37 counter.incrementAndGet();
38 }
39 }
40 }
41}
1100000000
2AtomicLong耗时:720
LongAdderDemo
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3import java.util.concurrent.atomic.AtomicLong;
4import java.util.concurrent.atomic.LongAdder;
5
6/**
7 * 描述: 演示高并发场景下,LongAdder比AtomicLong性能好
8 */
9public class LongAdderDemo {
10
11 public static void main(String[] args) throws InterruptedException {
12 LongAdder counter = new LongAdder();
13 ExecutorService service = Executors.newFixedThreadPool(20);
14 long start = System.currentTimeMillis();
15 for (int i = 0; i < 10000; i++) {
16 service.submit(new Task(counter));
17 }
18 service.shutdown();
19 while (!service.isTerminated()) {
20
21 }
22 long end = System.currentTimeMillis();
23 System.out.println(counter.sum());
24 System.out.println("LongAdder耗时:" + (end - start));
25 }
26
27 private static class Task implements Runnable {
28
29 private LongAdder counter;
30
31 public Task(LongAdder counter) {
32 this.counter = counter;
33 }
34
35 @Override
36 public void run() {
37 for (int i = 0; i < 10000; i++) {
38 counter.increment();
39 }
40 }
41 }
42}
43
1100000000
2LongAdder耗时:70
Accumulator 累加器
Accumulator 和 Adder 非常相似,Accumulator 就是一个更通用版本的 Adder。
它可以更灵活的书写计算逻辑,适用于大量计算,需要并发计算的场景。前提是:
1* 计算顺序不会影响最终计算结果
如果计算逻辑有严格的顺序,那么也不能使用此工具类。
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3import java.util.concurrent.atomic.LongAccumulator;
4import java.util.stream.IntStream;
5
6/**
7 * 描述: 演示LongAccumulator的用法
8 */
9public class LongAccumulatorDemo {
10
11 public static void main(String[] args) {
12 LongAccumulator accumulator = new LongAccumulator((x, y) -> 2 + x * y, 1);
13 ExecutorService executor = Executors.newFixedThreadPool(8);
14 IntStream.range(1, 10).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));
15
16 executor.shutdown();
17 while (!executor.isTerminated()) {
18
19 }
20 System.out.println(accumulator.getThenReset());
21 }
22}