目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

原子类

原子类

 原子是不可分割的最小单位,故原子类可以认为其操作都是不可分割。
 一个操作是不可中断的,即便是在多线程情况下也可以保证。
java.uti.concurrent.aomic 包下有很多具有原子特性的类。

原子类 VS 锁

  • 粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况,锁的粒度通常大于原子类。
  • 效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况下。

6 类原子类纵览

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
AtomicLong 的弊端

 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
LongAdder 带来的改进

 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}

作者:Soulboy