一、介绍
在java.util.concurrent.atomic包下atomic一般指原子操作类,主要分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。
二、简单使用
1.AtomicInteger
通过synchronized关键字来保证原子性,
private static int count = 0;
private static Object object = new Object();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
synchronized (object) {
for (int j = 0; j < 10000; j++) {
count++;
}
}
}).start();
}
Thread.sleep(1000);
System.out.println(count); // 100000
}
通过Atomic类来保证原子性,
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
count.incrementAndGet();
}
}).start();
}
Thread.sleep(1000);
System.out.println(count); // 100000
}
2.AtomicIntegerArray
static int[] value = new int[]{1,2};
static AtomicIntegerArray array = new AtomicIntegerArray(value);
public static void main(String[] args) {
array.getAndSet(0,3); // 将数组中的下标为0的元素修改为3
System.out.println(array.get(0)); // 3
System.out.println(value[0]); // 1,可见AtomicIntegerArray属于深拷贝,不影响原数组的取值
}
3.AtomicReferenceArray
@Data
public class User{
private String userName;
public User(String userName){
this.userName= userName;
}
}
static User[] value = new User[]{new User("张三"),new User("李四")};
static AtomicReferenceArray<User> array = new AtomicReferenceArray(value);
public static void main(String[] args) {
System.out.println(array.get(0).getUserName()); // 张三
array.set(0,new User("王五"));
System.out.println(array.get(0).getUserName()); // 王五
System.out.println(value[0].getUserName()); // 张三
}
@Data
public class User {
private String userName;
private Integer age;
public User() {
}
public User(String userName) {
this.userName = userName;
}
public User(String userName, Integer age) {
this.userName = userName;
this.age = age;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
static AtomicReferenceFieldUpdater atomic = AtomicReferenceFieldUpdater.newUpdater(User.class,String.class,"userName");
public static void main(String[] args) {
User user = new User("张三", 10);
atomic.set(user,"王五");
System.out.println(user.getUserName()); // 王五
}
需要将userName改为public String userName;
需要将userName改为public volatile String userName;
三、底层原理
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package java.util.concurrent.atomic;
import java.io.Serializable;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
import sun.misc.Unsafe;
public class AtomicInteger extends Number implements Serializable {
private static final long serialVersionUID = 6214790243416807050L;
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value属性在对象内存当中的偏移量
private static final long valueOffset;
private volatile int value;
public AtomicInteger(int var1) {
this.value = var1;
}
public AtomicInteger() {
}
public final int get() {
return this.value;
}
public final void set(int var1) {
this.value = var1;
}
public final void lazySet(int var1) {
unsafe.putOrderedInt(this, valueOffset, var1);
}
public final int getAndSet(int var1) {
return unsafe.getAndSetInt(this, valueOffset, var1);
}
public final boolean compareAndSet(int var1, int var2) {
return unsafe.compareAndSwapInt(this, valueOffset, var1, var2);
}
public final boolean weakCompareAndSet(int var1, int var2) {
return unsafe.compareAndSwapInt(this, valueOffset, var1, var2);
}
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
public final int getAndAdd(int var1) {
return unsafe.getAndAddInt(this, valueOffset, var1);
}
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
public final int addAndGet(int var1) {
return unsafe.getAndAddInt(this, valueOffset, var1) + var1;
}
public final int getAndUpdate(IntUnaryOperator var1) {
int var2;
int var3;
do {
var2 = this.get();
var3 = var1.applyAsInt(var2);
} while(!this.compareAndSet(var2, var3));
return var2;
}
public final int updateAndGet(IntUnaryOperator var1) {
int var2;
int var3;
do {
var2 = this.get();
var3 = var1.applyAsInt(var2);
} while(!this.compareAndSet(var2, var3));
return var3;
}
public final int getAndAccumulate(int var1, IntBinaryOperator var2) {
int var3;
int var4;
do {
var3 = this.get();
var4 = var2.applyAsInt(var3, var1);
} while(!this.compareAndSet(var3, var4));
return var3;
}
public final int accumulateAndGet(int var1, IntBinaryOperator var2) {
int var3;
int var4;
do {
var3 = this.get();
var4 = var2.applyAsInt(var3, var1);
} while(!this.compareAndSet(var3, var4));
return var4;
}
public String toString() {
return Integer.toString(this.get());
}
public int intValue() {
return this.get();
}
public long longValue() {
return (long)this.get();
}
public float floatValue() {
return (float)this.get();
}
public double doubleValue() {
return (double)this.get();
}
static {
try {
// 获得对象的偏移量
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception var1) {
throw new Error(var1);
}
}
}
四、Lock、synchronized、Atomic的区别
区别:
(1)ReentrantLock拥有Synchronized相同的并发性和内存语义,两者都是可重入的锁;Lock的实现依赖于cpu级别的指令控制,Synchronized的实现主要由JVM实现控制
(2)synchronized不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定;但是使用Lock则不行,lock是通过代码实现的,要人为保证锁定一定会被释放,就必须将unLock()放到finally{}中,否则可能造成死锁现象
(3)synchronized不是可中断锁,而ReentrantLock是可中断锁。Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
(4)通过ReentrantLock可以知道有没有成功获取锁,而synchronized却无法办到。
(5)synchronized是非公平锁,而ReentrantLock默认实现是非公平锁,但提供公平锁的实现;
(6)ReentrantLock提供读写两种锁操作。
性能比较:
(1)在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,原因在于,编译程序通常会尽可能的进行优化synchronized,另外可读性非常好;但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。
(2)Atomic: 和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为它不能在多个Atomic之间同步。
如何选用:
在JDK1.5中,synchronized的性能是比较低的,线程阻塞和唤醒由操作系统内核完成,频繁的加锁和放锁导致频繁的上下文切换,造成效率低下;因此在多线程环境下,synchronized的吞吐量下降的非常严重。但在JDK1.6时对synchronized进行了很多优化,包括偏向锁、自适应自旋、轻量级锁等措施(synchronized关键字会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,这个过程中涉及到操作系统用户模式和内核模式的转换,代价比较高。
尽管JAVA 1.6为synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在转变为重量级锁之后,性能仍然比较低。)。
当需要可定时的、可轮询的与可中断的锁获取操作,公平队列,或者非块结构的锁等高级特性时,才应该使用Lock:否则,请使用synchronized。所以,我们在写同步的时候,优先考虑synchronized,如果有特殊需要,再进一步优化。ReentrantLock和Atomic如果用的不好,不仅不能提高性能,还可能带来灾难。针对计数情况,我们就可以使用java中的“原子操作类”。