我的以为这一节掌握基本的使用便可!java
本节思惟导图:数组
Atomic 翻译成中文是原子的意思。在化学上,咱们知道原子是构成通常物质的最小单位,在化学反应中是不可分割的。在咱们这里 Atomic 是指一个操做是不可中断的。即便是在多个线程一块儿执行的时候,一个操做一旦开始,就不会被其余线程干扰。安全
因此,所谓原子类说简单点就是具备原子/原子操做特征的类。bash
并发包 java.util.concurrent
的原子类都存放在java.util.concurrent.atomic
下,以下图所示。多线程
根据操做的数据类型,能够将JUC包中的原子类分为4类并发
基本类型this
使用原子的方式更新基本类型atom
数组类型spa
使用原子的方式更新数组里的某个元素线程
引用类型
对象的属性修改类型
下面咱们来详细介绍一下这些原子类。
使用原子的方式更新基本类型
上面三个类提供的方法几乎相同,因此咱们这里以 AtomicInteger 为例子来介绍。
AtomicInteger 类经常使用方法
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) //若是输入的数值等于预期值,则以原子方式将该值设置为输入值(update) public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置以后可能致使其余线程在以后的一小段时间内仍是能够读到旧的值。 复制代码
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
int temvalue = 0;
AtomicInteger i = new AtomicInteger(0);
temvalue = i.getAndSet(3);
System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3
temvalue = i.getAndIncrement();
System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4
temvalue = i.getAndAdd(5);
System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9
}
}
复制代码
经过一个简单例子带你们看一下基本数据类型原子类的优点
①多线程环境不使用原子类保证线程安全(基本数据类型)
class Test {
private volatile int count = 0;
//若要线程安全执行执行count++,须要加锁
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
复制代码
②多线程环境使用原子类保证线程安全(基本数据类型)
class Test2 {
private AtomicInteger count = new AtomicInteger();
public void increment() {
count.incrementAndGet();
}
//使用AtomicInteger以后,不须要加锁,也能够实现线程安全。
public int getCount() {
return count.get();
}
}
复制代码
AtomicInteger 类的部分源码:
// setup to use Unsafe.compareAndSwapInt for updates(更新操做时提供“比较并替换”的做用)
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 (compare and swap) + volatile 和 native 方法来保证原子操做,从而避免 synchronized 的高开销,执行效率大为提高。
CAS的原理是拿指望的值和本来的一个值做比较,若是相同则更新成新的值。UnSafe 类的 objectFieldOffset() 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址,返回值是 valueOffset。另外 value 是一个volatile变量,在内存中可见,所以 JVM 能够保证任什么时候刻任何线程总能拿到该变量的最新值。
使用原子的方式更新数组里的某个元素
上面三个类提供的方法几乎相同,因此咱们这里以 AtomicIntegerArray 为例子来介绍。
AtomicIntegerArray 类经常使用方法
public final int get(int i) //获取 index=i 位置元素的值 public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增 public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减 public final int getAndAdd(int delta) //获取 index=i 位置元素的值,并加上预期的值 boolean compareAndSet(int expect, int update) //若是输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update) public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置以后可能致使其余线程在以后的一小段时间内仍是能够读到旧的值。 复制代码
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
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 为例子来介绍。
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 方法相同。运行上面的代码后的输出结果以下:
Daisy
20
复制代码
若是须要原子更新某个类里的某个字段时,须要用到对象的属性修改类型原子类。
要想原子地更新对象的属性须要两步。第一步,由于对象的属性修改类型原子类都是抽象类,因此每次使用都必须使用静态方法 newUpdater()建立一个更新器,而且须要设置想要更新的类和属性。第二步,更新的对象属性必须使用 public volatile 修饰符。
上面三个类提供的方法几乎相同,因此咱们这里以 AtomicIntegerFieldUpdater
为例子来介绍。
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));// 22
System.out.println(a.get(user));// 23
}
}
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;
}
}
复制代码
输出结果:
22
23
复制代码