JUC中的Atomic原子类
文章目錄
- Atomic 原子類
- 1. 原子類介紹
- 2. 基本類型原子類
- 3. 數組類型原子類
- 4. 引用類型原子類
- 5. 升級類型原子類
- 6. Adder 累加器
- 7. Accumulator 累加器
Atomic 原子類
1. 原子類介紹
不可分割的
一個操作是不可中斷的,即使多線程的情況下也可以保證, 即使是在多個線程一起執行的時候,一個操作一旦開始,就不會被其他線程干擾。
原子類的作用和鎖類似,是為了保證并發情況下線程安全,不過相比于鎖,更有優勢
優勢: 粒度更細,效率更高
原子類縱覽:
| Atomic*基本類型原子類 | AtomicInteger AtomicLong AtomicBoolean |
| Atomic*Arrays數組類型原子類 | AtomicIntegerArray AtomicLongArray AtomicReferenceArray |
| Atomic*Reference引用類型原子類 | AtomicReference AtomicStampedReference AtomicMarkableReference |
| Atomic*Fieldupdate升級類型原子類 | AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater |
| Adder累加器 | LongAdder DoubleAdder |
| Accumulator累加器 | LongAccumulator DoubleAccumulator |
2. 基本類型原子類
- AtomicInteger :整型原子類
- AtomicLong :長整型原子類
- AtomicBoolean :布爾型原子類
以 AtomicInteger 為例
AtomicInteger 類常用方法 :
public final int get() //獲取值public final void set(int newValue) // 設置值public final void lazySet(int newValue) //最終設置為給定的值public final int getAndSet(int newValue) // 獲取當前值,并設置新值public final int getAndIncrement() //獲取當前值 并自增public final int getAndDecrement() //獲取當前的值,并自減public final int getAndAdd(int delta) // 獲取當前值,并加上預期值public final int getAndAdd(int delta) // 獲取當前值,并加上預期值public final boolean compareAndSet(int expect, int update) //比較并替換使用 :
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {public static void main(String[] args) {int temvalue = 0;AtomicInteger i = new AtomicInteger(0);temvalue = i.getAndSet(3);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3temvalue = i.getAndIncrement();System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4temvalue = i.getAndAdd(5);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9}}案例 :
public class Test {public static void main(String[] args) {TestDemo thread = new TestDemo();Thread t1 = new Thread(thread,"窗口一");Thread t2 = new Thread(thread,"窗口二");t1.start();t2.start();} }class TestDemo implements Runnable{//共享的火車票變量private int count = 100;//重寫run方法@Overridepublic void run() {while (count > 0){try {//休眠一下 方便出現并發問題Thread.sleep(50);}catch (Exception e){e.getMessage();}sale();}}//賣票public void sale(){if(count > 0){System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));count--;}}}窗口一出售 :37
窗口二出售 :39
窗口一出售 :40
窗口二出售 :41
窗口一出售 :41
窗口一出售 :43
窗口二出售 :43
窗口一出售 :45
窗口二出售 :45
窗口一出售 :47
窗口二出售 :47
多線程下會出現重復賣票的情況,我們解決這個問題可以使用 JDK 內置鎖 (Synchronized)保證線程原子性,當某個線程取到鎖后,其他線程就會等待,但是性能低下,我們可以使用AtomicInteger類,是一個專門提供可以保證原子性的類
package com.dimple.test;import java.util.concurrent.atomic.AtomicInteger;public class Test5 {public static void main(String[] args) {TestDemo thread = new TestDemo();Thread t1 = new Thread(thread,"窗口一");Thread t2 = new Thread(thread,"窗口二");t1.start();t2.start();} }class TestDemo implements Runnable{//共享的火車票變量private static AtomicInteger atomic = new AtomicInteger(100);//重寫run方法@Overridepublic void run() {while (atomic.get() > 0){try {//休眠一下 方便出現并發問題Thread.sleep(50);}catch (Exception e){e.getMessage();}sale();}}//賣票public void sale(){if(atomic.get() > 0){Integer count= 100 - atomic.getAndDecrement() + 1; //使用底層方法getAndDecrement() 自-1;System.out.println(Thread.currentThread().getName()+ "," + count);//獲取當前值}} }窗口一出售 :91
窗口二出售 :92
窗口一出售 :93
窗口二出售 :94
窗口一出售 :95
窗口二出售 :96
窗口一出售 :97
窗口二出售 :98
窗口一出售 :99
窗口二出售 :100
原理分析 :
// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;public AtomicInteger(int initialValue) {value = initialValue;}3. 數組類型原子類
- AtomicIntegerArray :整形數組原子類
- AtomicLongArray :長整形數組原子類
- AtomicReferenceArray :引用類型數組原子類
以 AtomicIntegerArray 為例
AtomicIntegerArray 類常用方法
public final int get(int i) //獲取值public final void set(int i, int newValue) // 設置值public final void lazySet(int i, int newValue) //最終集的元素在位置 i到給定值。public final int getAndSet(int i, int newValue) //自動設置元素的位置 i到給定值并返回舊值。public final boolean compareAndSet(int i, int expect, int update) //自動設置元素的位置 i給更新后的值,如果預期值 ==期望值。public final int getAndIncrement(int i) //自動遞增一個指數 i元素。public final int getAndDecrement(int i) //自動遞減指數 i元素使用 :
import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayTest {public static void main(String[] args) {int temvalue = 0;int[] nums = { 1, 2, 3, 4, 5, 6 };AtomicIntegerArray i = new AtomicIntegerArray(nums);for (int j = 0; j < nums.length; j++) {System.out.println(i.get(j));}temvalue = i.getAndSet(0, 2);System.out.println("temvalue:" + temvalue + "; i:" + i);temvalue = i.getAndIncrement(0);System.out.println("temvalue:" + temvalue + "; i:" + i);temvalue = i.getAndAdd(0, 5);System.out.println("temvalue:" + temvalue + "; i:" + i);}}案例 :
public class AtmoicArray {public static void main(String[] args) throws InterruptedException {AtomicIntegerArray atomicIntegerArray=new AtomicIntegerArray(1000);Decrement decrement = new Decrement(atomicIntegerArray);Increment increment = new Increment(atomicIntegerArray);Thread[] threads= new Thread[100];Thread[] threads2= new Thread[100];for (int i = 0; i < 100 ; i++) {threads2[i]=new Thread(decrement);threads[i]=new Thread(increment);threads2[i].start();threads[i].start();}for (int i = 0; i < 100 ; i++) {threads2[i].join();threads[i].join();}for (int i = 0; i < atomicIntegerArray.length(); i++) {if(atomicIntegerArray.get(i)!=0) {System.out.println("發現非0值" + i);}}System.out.println("運行結束");}}class Decrement implements Runnable{private AtomicIntegerArray array;Decrement(AtomicIntegerArray array) {this.array = array;}@Overridepublic void run() {for (int i = 0; i < array.length() ; i++) {array.getAndDecrement(i);}} }class Increment implements Runnable{private AtomicIntegerArray array;Increment(AtomicIntegerArray array) {this.array = array;}@Overridepublic void run() {for (int i = 0; i < array.length() ; i++) {array.getAndIncrement(i);}} }運行結束
運行結果 ,會發現我們數組線程每次 加100 減100 ,并不會出現不等于0的數據,數據并沒有出現錯亂,AtomicIntegerArray 給我們提供了數組的原子性
原理分析 :
private static final Unsafe unsafe = Unsafe.getUnsafe();private static final int base = unsafe.arrayBaseOffset(int[].class);private static final int shift;private final int[] array;static {int scale = unsafe.arrayIndexScale(int[].class);if ((scale & (scale - 1)) != 0)throw new Error("data type scale not a power of two");shift = 31 - Integer.numberOfLeadingZeros(scale);}private long checkedByteOffset(int i) {if (i < 0 || i >= array.length)throw new IndexOutOfBoundsException("index " + i);return byteOffset(i);}private static long byteOffset(int i) {return ((long) i << shift) + base;}4. 引用類型原子類
基本類型原子類只能更新一個變量,如果需要原子更新多個變量,需要使用引用類型原子類
- AtomicReference :引用類型原子類
- AtomicStampedReference :原子更新帶有版本號的引用類型
- AtomicMarkableReference :原子更新帶有標記的引用類型
AtomicReference 常用方法
public final V get() //獲取值public final void set(V newValue) //設置值public final void lazySet(V newValue) //最終設置為給定的值public final boolean compareAndSet(V expect, V update) //自動設置的值來指定更新值public final V getAndSet(V newValue) //自動設置為給定的值并返回舊值。public final V getAndUpdate(UnaryOperator<V> updateFunction) //自動更新當前值與結果應用給定的函數,返回前一個值。AtomicReference 使用
public class Test {public static void main(String[] args) {AtomicReference<Person> atomicReference = new AtomicReference<Person>();Person person = new Person("abc", 22);atomicReference.set(person);Person updatePerson = new Person("Daisy", 20);atomicReference.compareAndSet(person, updatePerson);System.out.println(atomicReference.get().getName());System.out.println(atomicReference.get().getAge());}}@Data class Person {private String name;private int age;}Daisy
20
AtomicStampedReference 使用
public class Test {public static void main(String[] args) {// 實例化、取當前值和 stamp 值final Integer initialRef = 0, initialStamp = 0;final AtomicStampedReference<Integer> asr = new AtomicStampedReference<Integer>(initialRef, initialStamp);System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());// compare and setfinal Integer newReference = 666, newStamp = 999;final boolean casResult = asr.compareAndSet(initialRef, newReference, initialStamp, newStamp);System.out.println("currentValue=" + asr.getReference()+ ", currentStamp=" + asr.getStamp()+ ", casResult=" + casResult);// 獲取當前的值和當前的 stamp 值int[] arr = new int[1];final Integer currentValue = asr.get(arr);final int currentStamp = arr[0];System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp);// 單獨設置 stamp 值final boolean attemptStampResult = asr.attemptStamp(newReference, 88);System.out.println("currentValue=" + asr.getReference()+ ", currentStamp=" + asr.getStamp()+ ", attemptStampResult=" + attemptStampResult);// 重新設置當前值和 stamp 值asr.set(initialRef, initialStamp);System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());}currentValue=0, currentStamp=0
currentValue=666, currentStamp=999, casResult=true
currentValue=666, currentStamp=999
currentValue=666, currentStamp=88, attemptStampResult=true
currentValue=0, currentStamp=0
AtomicMarkableReference 使用
public class Test {public static void main(String[] args) {// 實例化、取當前值和 mark 值final Boolean initialRef = null, initialMark = false;final AtomicMarkableReference<Boolean> amr = new AtomicMarkableReference<Boolean>(initialRef, initialMark);System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());// compare and setfinal Boolean newReference1 = true, newMark1 = true;final boolean casResult = amr.compareAndSet(initialRef, newReference1, initialMark, newMark1);System.out.println("currentValue=" + amr.getReference()+ ", currentMark=" + amr.isMarked()+ ", casResult=" + casResult);// 獲取當前的值和當前的 mark 值boolean[] arr = new boolean[1];final Boolean currentValue = amr.get(arr);final boolean currentMark = arr[0];System.out.println("currentValue=" + currentValue + ", currentMark=" + currentMark);// 單獨設置 mark 值final boolean attemptMarkResult = amr.attemptMark(newReference1, false);System.out.println("currentValue=" + amr.getReference()+ ", currentMark=" + amr.isMarked()+ ", attemptMarkResult=" + attemptMarkResult);// 重新設置當前值和 mark 值amr.set(initialRef, initialMark);System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());}currentValue=null, currentMark=false
currentValue=true, currentMark=true, casResult=true
currentValue=true, currentMark=true
currentValue=true, currentMark=false, attemptMarkResult=true
currentValue=null, currentMark=false
5. 升級類型原子類
- AtomicIntegerFieldUpdater : 原子更新整形字段的更新器
- AtomicLongFieldUpdater :原子更新長整形字段的更新器
- AtomicReferenceFieldUpdater :原子更新引用類型里的字段的更新器
AtomicIntegerFieldUpdater 常用方法
public final V get() //獲取值public final void set(V newValue) //設置值public final void lazySet(V newValue) //最終設置為給定的值public final boolean compareAndSet(V expect, V update) //自動設置的值來指定更新值public final V getAndSet(V newValue) //自動設置為給定的值并返回舊值。public final V getAndUpdate(UnaryOperator<V> updateFunction) //自動更新當前值與結果應用給定的函數,返回前一個值。public final V getAndAccumulate(T obj, V x,BinaryOperator<V> accumulatorFunction) //自動更新與應用給出的函數的值與給定值的結果指標 i元素,返回前一個值。public final V accumulateAndGet(T obj, V x,BinaryOperator<V> accumulatorFunction) //自動更新與應用給出的函數的值與給定值的結果指標 i元素,返回更新后的值。AtomicIntegerFieldUpdater 使用
public class Test {public static void main(String[] args) {AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");User user = new User("Java", 22);System.out.println(a.getAndIncrement(user));// 22System.out.println(a.get(user));// 23} }@Data class User {private String name;public volatile int age; }22
23
要想原子地更新對象的屬性需要兩步。第一步,因為對象的屬性修改類型原子類都是抽象類,所以每次使用都必須使用靜態方法 newUpdater()創建一個更新器,并且需要設置想要更新的類和屬性。第二步,更新的對象屬性必須使用 public volatile 修飾符。
6. Adder 累加器
- 是JDK 1.8 中 引入的一個比較新的類
- 高并發的情況下 LongAdder 比 AtomitLong 效率高,不過是空間換時間
- 競爭激烈的時候,LingAdder 把不同的線程對應到不同的cell 上修改,降低了沖突的概率,是多段鎖的理念,提高了并發性
測試 AtomicLong的性能
import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong;/*** 演示高并發情況下LongAdder 比 AtomicLong* 性能好*/ public class AtomicLongDemo {public static void main(String[] args) throws InterruptedException {AtomicLong atomicLong = new AtomicLong(0);//線程池開始時間long start = System.currentTimeMillis();ExecutorService executorService = Executors.newFixedThreadPool(20);for (int i = 0; i < 10000; i++) {executorService.submit(new Task(atomicLong));}//表示線程池執行完畢executorService.shutdown();while (!executorService.isTerminated()){}long end = System.currentTimeMillis();System.out.println(atomicLong.get());System.out.println("耗時"+(end -start));}public static class Task implements Runnable{private AtomicLong atomicLong;public Task(AtomicLong atomicLong) {this.atomicLong = atomicLong;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {atomicLong.incrementAndGet();}}} }測試 LongAdder 性能
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder;/*** 演示高并發情況下LongAdder 比 AtomicLong* 性能好*/ public class LongAdderDemo {public static void main(String[] args) throws InterruptedException {LongAdder atomicLong = new LongAdder();//線程池開始時間long start = System.currentTimeMillis();ExecutorService executorService = Executors.newFixedThreadPool(20);for (int i = 0; i < 10000; i++) {executorService.submit(new Task(atomicLong));}//表示線程池執行完畢executorService.shutdown();while (!executorService.isTerminated()){}long end = System.currentTimeMillis();System.out.println(atomicLong.sum() );System.out.println("耗時"+(end -start));}public static class Task implements Runnable{private LongAdder longAdder;public Task(LongAdder longAdder) {this.longAdder = longAdder;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {longAdder.increment();}}} }會發現 LongAdder 比 AtomicLong 快了好多
7. Accumulator 累加器
- Accumualtor 和 Adder 非常相似,Accumualtor就是更通用的版本Adder
45
個人博客地址:http://blog.yanxiaolong.cn ? | 『縱有疾風起,人生不言棄』
總結
以上是生活随笔為你收集整理的JUC中的Atomic原子类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你见过出道即巅峰吗?
- 下一篇: ARM指令集之跳转指令