Atomic原子类 Atomic
是基于unsafe
类和自旋操作实现的,要理解Atomic
首先需要理解CAS。Atomic是指一个操作是不可中断的,即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
所以,所谓源自类就说具有原子/原子操作特征的类。根据操作的数据类型,可以讲JUC包中的原子类分为4类。
要理解Atomic首先得了解CAS,CAS (Compare and Swap),其功能就是判断内存中的某个值是否与预期的值相等,相等就用新值更新旧值,否则就不更新。Java中CAS是基于unsafe
类首先的,所有的unsafe
类中的方法都是native(原生)方法,直接调用操作系用底层资源执行任务。
基本类型
使用原子的方式更新基本类型
AtomicInteger
:整形原子类
AtomicLong
:长整型原子类
AtomicBoolean
:布尔型原子类
数组类型
使用原子的方式更新数组里的某个元素
AtomicIntegerArray
:整形数组原子类
AtomicLongArray
:长整型数组原子类
AtomicReferenceArray
:引用类型数组原子类
引用类型
对象的属性修改类型
AtomicIntegerFieldUpdater
:原子更新整型字段的更新器
AtomicLongFieldUpdater
:原子更新长整型字段的更新器
AtomicReferenceFieldUpdater
:原子更新引用类型里的字段
基本类型原子类 以AtomicInteger
为例,其常用类方法如下:
1 2 3 4 5 6 7 public final int get () public final int getAndSet (int newValue) public final int getAndIncrement () public final int getAndDecrement () public final int getAndAdd (int delta) boolean compareAndSet (int expect, int update) public final void lazySet (int newValue)
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerDemo { 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 = i.getAndIncrement(); System.out.println("temvalue:" + temvalue + "; i:" + i); temvalue = i.getAndAdd(5 ); System.out.println("temvalue:" + temvalue + "; i:" + i); } }
基本数据类型原子类的优势
多线程环境不使用原子类保证线程安全
需要使用synchronized ,示例如下:
1 2 3 4 5 6 7 8 9 10 11 class Test { private volatile int count = 0 ; public synchronized void increment () { count++; } public int getCount () { return count; } }
多线程环境使用原子类保证线程安全(基本数据类型)
1 2 3 4 5 6 7 8 9 10 11 class Test2 { private AtomicInteger count = new AtomicInteger (); public void increment () { count.incrementAndGet(); } public int getCount () { return count.get(); } }
AtomicInteger线程安全原理分析 部分源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 private 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;
AtomicInteger类主要利用CAS+volatile和native方法来保证原子操作,从而避免使用synchronized的高开销,执行效率大为提升。
CAS就是拿期望值和原来的值进行比较,如果相同就更新,如果不相同就不更新。Unsafe类的objectFieldOffset()
方法是一个native方法,这个方法是用来拿到旧值的内存地址,另外value是一个volatile变量,在内存中可见,因此JVM可以保证在任何时刻任何线程总能拿到该变量的最新值。
数组类型原子类 数组类型原子类介绍 使用原子的方式更新数组里的某个元素
AtomicIntegerArray
:整形数组原子类
AtomicLongArray
:长整型数组原子类
AtomicReferenceArray
:引用数组类型原子类
上面三个类提供的方法几乎相同,这边以AtomicIntegerArray
为例进行介绍。
AtomicIntegerArray
类常用方法
1 2 3 4 5 6 7 public final int get (int i) public final int getAndSet (int i, int newValue) public final int getAndIncrement (int i) public final int getAndDecrement (int i) public final int getAndAdd (int i, int delta) boolean compareAndSet (int i, int expect, int update) public final void lazySet (int i, int newValue)
AtomicIntegerArray
常见方法的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); } }
引用类型原子类 引用类型原子类介绍 基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用引用类型原子类。
AtomicReference
:引用类型原子类
AtomicStampedReference
:原子更新带有版本号的引用类型。该类将整数值与引用类型关联起来,可用于解决原子更新数据和数据的版本后,可以解决使用CAS进行原子更新时可能出现的ABA问题。
AtomicMarkableReference
:原子更新带有标记的引用类型。该类将boolean标记与引用类型关联起来。
上面三个类可以提供的方法几乎相同,接下来以AtomicReference
为例介绍:
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 import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceTest { public static void main (String[] args) { AtomicReference<Person> ar = new AtomicReference <Person>(); Person person = new Person ("SnailClimb" , 22 ); ar.set(person); Person updatePerson = new Person ("Daisy" , 20 ); ar.compareAndSet(person, updatePerson); System.out.println(ar.get().getName()); System.out.println(ar.get().getAge()); } } class Person { private String name; private int age; public Person (String name, int age) { super (); this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } }
结果如下:
上诉代码首先创建了一个Person对象,然后把Person对象设置进AtomicReference
对象中,然后调用compareAndSet方法,该方法就是通过CAS操作设置ar。如果ar的值为person的话,则将其设置为updatePerson。实现原理与AtomicInteger
类中的compareAndSet方法相同。
AtomicStampedReference
类使用示例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 import java.util.concurrent.atomic.AtomicStampedReference;public class AtomicStampedReferenceDemo { public static void main (String[] args) { final Integer initialRef = 0 , initialStamp = 0 ; final AtomicStampedReference<Integer> asr = new AtomicStampedReference <>(initialRef, initialStamp); System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp()); final 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); int [] arr = new int [1 ]; final Integer currentValue = asr.get(arr); final int currentStamp = arr[0 ]; System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp); final boolean attemptStampResult = asr.attemptStamp(newReference, 88 ); System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp() + ", attemptStampResult=" + attemptStampResult); asr.set(initialRef, initialStamp); System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp()); final boolean wCasResult = asr.weakCompareAndSet(initialRef, newReference, initialStamp, newStamp); System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp() + ", wCasResult=" + wCasResult); } }
输出结果如下:
1 2 3 4 5 6 currentValue=0 , currentStamp=0 currentValue=666 , currentStamp=999 , casResult=true currentValue=666 , currentStamp=999 currentValue=666 , currentStamp=88 , attemptStampResult=true currentValue=0 , currentStamp=0 currentValue=666 , currentStamp=999 , wCasResult=true
AtomicMarkableReference
类使用示例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 import java.util.concurrent.atomic.AtomicMarkableReference;public class AtomicMarkableReferenceDemo { public static void main (String[] args) { final Boolean initialRef = null , initialMark = false ; final AtomicMarkableReference<Boolean> amr = new AtomicMarkableReference <>(initialRef, initialMark); System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked()); final 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); boolean [] arr = new boolean [1 ]; final Boolean currentValue = amr.get(arr); final boolean currentMark = arr[0 ]; System.out.println("currentValue=" + currentValue + ", currentMark=" + currentMark); final boolean attemptMarkResult = amr.attemptMark(newReference1, false ); System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked() + ", attemptMarkResult=" + attemptMarkResult); amr.set(initialRef, initialMark); System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked()); final boolean wCasResult = amr.weakCompareAndSet(initialRef, newReference1, initialMark, newMark1); System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked() + ", wCasResult=" + wCasResult); } }
输出结果如下:
1 2 3 4 5 6 currentValue=null , currentMark=false currentValue=true , currentMark=true , casResult=true currentValue=true , currentMark=true currentValue=true , currentMark=false , attemptMarkResult=true currentValue=null , currentMark=false currentValue=true , currentMark=true , wCasResult=true
对象的属性修改类型原子类 如果需要原子更新某个类的某个字段时,需要用到对象的属性修改类型原子类。
AtomicIntegerFieldUpdater
:原子更新整形字段的更新器
AtomicLongFieldUpdater
:原子更新长整形字段的更新器
AtomicReferenceFieldUpdater
:原子更新引用类型里的字段的更新器
原子地更新对象的属性需要两步。第一步,因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性;第二步,更新的对象属性必须使用public volatile修饰符。
上面三个类提供的方法几乎相同,所以我们这里以AtomicIntegerFieldUpdater
为例子来介绍。
AtomicIntegerFieldUpdater
类示例代码:
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 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;public class AtomicIntegerFieldUpdaterTest { 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)); System.out.println(a.get(user)); } } class User { private String name; public volatile int age; public User (String name, int age) { super (); this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } }
输出结果:
CAS与synchronized比较 CAS支持多个线程并发修改,并发程度高,而synchronized一次只有一个线程修改,并发程度低;
CAS只支持一个共享变量的原子操作;
synchronized可以对多个变量进行加锁;
CAS会出现ABA问题(可以解决);