Atomic 原子类
第四章 原子类
1、什么是原子类?
1、不可分割;
2、一个操作是不可中断的,即便是多线程的情况下也可以保证。
2、有什么作用?
1、原子类的作用和锁类似,是为了保证并发情况下线程安全。不过原子类相比于锁,有一定的优势:
1)粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况了,通常锁的粒度都要大于原子变量的粒度;
2)效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况。
3、六类原子类
1、六类原子类纵览
2、基本类型原子类,以 AtomicInteger 为例
1)常用方法:
- get():获取当前的值,返回 int 类型
- getAndSet(int newValue):获取当前的值,并设置新的值,返回 int 类型
- getAndIncrement():获取当前的值,并自增,返回 int 类型
- getAndDecrement():获取当前的值,并自减,返回 int 类型
- getAndAdd(int delta):获取当前的值,并加上预期的值,返回 int 类型
- compareAndSet(int expect,int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入值,返回 boolean 类型
示例:
public class AtomicIntegerDemo1 { private static final AtomicInteger atomicInteger = new AtomicInteger(); private static ExecutorService service = Executors.newFixedThreadPool(5); public static void incrementAtomic() { //自减 //atomicInteger.getAndDecrement(); //自增 atomicInteger.getAndIncrement(); //如果值为100,则修改他为 8 atomicInteger.compareAndSet(100,8); } public static void main(String[] args) { for (int i = 0; i <100 ; i++) { int finalI = i; service.execute(() -> { incrementAtomic(); }); } service.shutdown(); while(true){ if(service.isTerminated()){ System.out.println(atomicInteger.get()); break; } } } }
2、数组类型原子类,以 AtomicIntegerArray 为例
演示:
public class AtomicArrayDemo { public static void main(String[] args) throws InterruptedException { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(100); //调用自增的线程 Thread[] threads1 = new Thread[100]; //调用自减的线程 Thread[] threads2 = new Thread[100]; //初始化线程 for (int i = 0; i < 100; i++) { int finalI = i; threads1[i] = new Thread(() -> decrement(atomicIntegerArray, finalI)); threads2[i] = new Thread(() -> increment(atomicIntegerArray, finalI)); } //调用自增自减,按照原子性的特点,数组中的所有值都应该为 0 for (int i = 0; i < 100; i++) { threads1[i].start(); threads2[i].start(); } Thread.sleep(5000); for (int i = 0; i < 100; i++) { System.out.println(atomicIntegerArray.get(i)); if (atomicIntegerArray.get(i) != 0) System.out.println("结果不为 0 就错了!!!"); } } //自减 public static void decrement(AtomicIntegerArray array,int count){ array.getAndDecrement(count); } //自增 public static void increment(AtomicIntegerArray array,int count){ array.getAndIncrement(count); } }
运行结果都为: 0
3、引用类型原子类,以 AtomicReference 为例
1)AtomicReference:AtomicReference 类的作用,和 AtomicInteger并没有本质区别,AtomicInteger可以让一个整数保证原子性,而 AtomicReference 可以让一个对象保证原子性,当然,AtomicReference 的功能明显比 AtomicInteger强,因为一个对象里可以包含很多属性。用法和 AtomicInteger 类似。
演示:
public class AtomicReferenceDemo2 { private static final AtomicReference atomicReference = new AtomicReference(); public static void getAndSet(Student user){ atomicReference.getAndSet(user); } public static void main(String[] args) throws InterruptedException { new Thread(() -> { getAndSet(new Student("张三")); System.out.println(atomicReference.get()); },"Thread-01").start(); new Thread(() -> { getAndSet(new Student("李四")); System.out.println(atomicReference.get()); },"Thread-02").start(); Thread.sleep(2000); System.out.println(atomicReference.get()); } } class Student{ public String name; @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student() { } public Student(String name) { this.name = name; } }
4、把普通变量升级为具有原子功能
1、AtomicIntegerFieldUpdater 对普通变量进行升级,升级后具有原子性
示例:
public class AtomicIntegerFieldUpdaterDemo3 { public static class Candidate{ //要升级的变量不能用 static 和 private 修饰,否则报错 volatile int score; } static Candidate tom; static Candidate peter; //利用反射原理 public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score"); public static void main(String[] args) throws InterruptedException { tom = new Candidate(); peter = new Candidate(); Thread thread1 = new Thread(() -> { for (int i = 0; i < 10000; i++) { tom.score++; scoreUpdater.getAndIncrement(peter); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i < 10000; i++) { tom.score++; scoreUpdater.getAndIncrement(peter); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("升级后的值:"+peter.score); System.out.println("原本的值:"+tom.score); } }
运行结果:
2、AtomicIntegerFieldUpdater 注意点
1)可见范围(要升级的变量不能使用 private 修饰,否则报错);
2)不支持 static 修饰(要升级的变量不能使用 static修饰,否则报错)。
3、Adder 累加器
1)是 Java8 引入的;
2)高并发下 LongAdder 比 AtomicLong 效率高,不过本质是空间换时间;
3)竞争激烈的时候,LongAdder 把不同线程对应到不同的 cell 上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性。
LongAdder 示例:
public class LongAdderDemo5 { public static void main(String[] args) { LongAdder counter = new LongAdder(); ExecutorService service = Executors.newFixedThreadPool(10); 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("LongAdder耗时:" + (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(); } } } }
运行结果:
AtomicLong 示例:
public class AtomicLongDemo4 { public static void main(String[] args) { AtomicLong counter = new AtomicLong(0); ExecutorService service = Executors.newFixedThreadPool(10); 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("AtomicLong耗时:" + (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.incrementAndGet(); } } } }
运行结果
根据运行结果知道,再高并发情况下 LongAdder 耗时 245 毫秒,AtomicLong 耗时 1616 毫秒。
4、AtomicLong 和 LongAdder
1)再低争用下,AtomicLong 和 LongAdder 这两个类具有相似的特征。但是在竞争激烈的情况下,LongAdder 的预期吞吐量要高的多,但要消耗更多的空间;
2)LongAdder 适合的场景是统计和计数的场景,而且 LongAdder 基本只提供了 add 方法,而 AtomicLong 还具有 CAS 方法。
5、Accumulator 累加器
1、Accumulator 和 Adder 非常相似,Accumulator 就是一个更通用版本的 Adder
示例:
public class LongAccumulatorDemo { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(5); /* x初始为0,y的值由accumulator.accumulate(y)设定 调用accumulator.accumulate(y)后计算结果,并把结果赋给x,可以继续调用accumulator.accumulate(y)来继续进行计算 调用accumulator.getThenReset()来获取当前计算出的结果 */ LongAccumulator accumulator = new LongAccumulator((x,y) -> 2*(x+y),0); for (int i = 0; i < 10; i++) { service.execute(() -> { accumulator.accumulate(2); }); } service.shutdown(); while(!service.isTerminated()){ } System.out.println(accumulator.getThenReset());//4092 } }
Accumulator 适用于并发情况下使用,效率更高,如果单线程计算的话,可以使用 for 循环计算。