PS:本文基于JDK8
Java从JDK5之后,可以使用java.util.concurrent.atomic包在无锁的情况下进行原子操作,通过Java的Unsafe工具类型调用CPU的原子指令完成,因CPU架构和指令的不同,也有可能出现堵塞的问题,通過自旋 + CAS(乐观锁)保证原子性。
其原理是使用CAS(Compare and Swap)算法,即比较再交换,在这个过程中,通过compareAndSwapInt比较更新value值,如果更新失败,重新获取旧值,然后更新。是一种无锁的非阻塞算法的实现,是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。JUC包中的类使用CAS算法实现了一种乐观锁,JDK5之前是使用synchronized关键字保证同步,这是一种独占锁,也是是悲观锁。(可参考:悲观锁和乐观锁)
原子操作只能使用在单线程中,在多线程中可能出现“ABA问题”,可以使用标记原子引用类型AtomicStampedReference或AtomicMarkableReference处理解决。
atomic包中共有18个操作类,可分为六大类型:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;
public class BaseAtomic {
/**
* 初始化为0
*/
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) {
//设置当前值为1
atomicInteger.set(1);
//返回当前值;
atomicInteger.get();
//返回当前值并做原子加1;
atomicInteger.getAndIncrement();
//做原子加1并返回变更后的值;
atomicInteger.incrementAndGet();
//返回当前值并做原子加n;
atomicInteger.getAndAdd(10);
//做原子加n并返回变更后的值;
atomicInteger.addAndGet(10);
//返回当前值并做原子减1;
atomicInteger.getAndDecrement();
//做原子减1并返回变更后的值;
atomicInteger.decrementAndGet();
//设置为2,本地内存操作,不保证线程可见
atomicInteger.lazySet(2);
//原子性函数修改后并返回变更后的值
IntUnaryOperator intUnaryOperator = operand -> operand + 2;
atomicInteger.updateAndGet(intUnaryOperator);
}
}
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.function.LongBinaryOperator;
public class ArrayAtomic {
/**
* 初始化为数组长度
*/
private static AtomicLongArray atomicLongArray = new AtomicLongArray(2);
public static void main(String[] args) {
//输出数组长度
atomicLongArray.length();
//设置数组索引0上的值为1
atomicLongArray.set(0, 1L);
//获取数组索引上的值
atomicLongArray.get(0);
//数组索引0上的值加1,并返回变更后的结果
atomicLongArray.incrementAndGet(0);
//返回数组索引0上的值,并在数组中的值加1
atomicLongArray.getAndIncrement(0);
//数组索引0上的值减1,并返回变更后的结果
atomicLongArray.decrementAndGet(0);
//返回数组索引0上的值,并在数组中的值减1
atomicLongArray.getAndDecrement(0);
//数组索引0上的值加2,并返回变更后的结果
atomicLongArray.addAndGet(0, 2L);
//返回数组索引0上的值,并在数组中的值加2
atomicLongArray.getAndAdd(0, 2L);
//数组中索引0上的数与1进行比较,
//相同着返回true,并修改数组中的值为3;
//不相同返回false,不做修改.
atomicLongArray.compareAndSet(0, 1L, 3L);
//与compareAndSet一致
atomicLongArray.weakCompareAndSet(0, 1L, 3L);
//变更函数
LongBinaryOperator longBinaryOperator = (left, right) -> left+right;
//返回数组中索引0上的值,并根据函数结果变更数组中的值
atomicLongArray.getAndAccumulate(0, 2L, longBinaryOperator);
//根据函数结果变更数组中的值,并返回数组中索引0上变更后的值
atomicLongArray.accumulateAndGet(0, 2L, longBinaryOperator);
}
}
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
public class ReferenceAtomic {
/**
* 初始化为0
*/
private static AtomicReference reference = new AtomicReference<>();
public static void main(String[] args) {
User user1 = new User("a", 1);
//设置引用对象为user
reference.set(user1);
//本地内存设置,不保证线程可见
reference.lazySet(user1);
//获取user对象
reference.get();
//获取并变更为user2
User user2 = new User("b", 2);
reference.getAndSet(user2);
//一元操作函数
UnaryOperator unaryOperator = new UnaryOperator(){
@Override
public Object apply(Object o) {
User user1 = (User) o;
user1.setAge(11);
return user1;
}
};
//返回当前引用,并对将引用对象修改为一元操作函数结果
reference.getAndUpdate(unaryOperator);
//二元擦破在函数
BinaryOperator binaryOperator = new BinaryOperator(){
/**
*
* @param o 变更前引用对象
* @param o2 传入的对象
* @return
*/
@Override
public Object apply(Object o, Object o2) {
User user1 = (User) o;
User user2 = (User) o2;
user1.setAge(user2.age);
return user1;
}
};
//获取当前引用对象,并将引用对象更新为二元函数返回的对象.
reference.getAndAccumulate(user1,binaryOperator);
//将引用对象更新为二元函数返回的对象,并获取变更后的引用对象
reference.accumulateAndGet(user2,binaryOperator);
//当前引用对象与user2比较
//相等返回true,并设置引用对象为user2
//不相等返回false,不做变更
reference.compareAndSet(user1,user2);
//与compareAndSet一致
reference.weakCompareAndSet(user1,user1);
}
private static class User {
private String userName;
volatile int age;
User(String userName, int age) {
this.userName = userName;
this.age = age;
}
void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
public class ReferenceFieldAtomic {
/**
* 初始化为0
*/
private static final AtomicIntegerFieldUpdater REFERENCE = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
public static void main(String[] args) {
User user = new User("a", 1);
//设置引用对象为user属性age为2
REFERENCE.set(user,2);
//本地内存设置,不保证线程可见
REFERENCE.lazySet(user,2);
//返回user对象中的age
REFERENCE.get(user);
//返回user对象的age属性,并变更为age为2
REFERENCE.getAndSet(user,2);
//返回对象user属性age的值,并修改age加1
REFERENCE.getAndIncrement(user);
//修改对象user属性age的值加1,并返回age
REFERENCE.incrementAndGet(user);
//返回对象user属性age的值,并修改age减1
REFERENCE.getAndDecrement(user);
//修改对象user属性age的值减1,并返回age
REFERENCE.decrementAndGet(user);
//一元操作函数
IntUnaryOperator intUnaryOperator = operand -> ++operand;
//返回当前user对象的age值,并将user的age值修改为一元操作函数结果
REFERENCE.getAndUpdate(user,intUnaryOperator);
//将user的age值修改为一元操作函数结,并返回更改后user对象的age值
REFERENCE.updateAndGet(user,intUnaryOperator);
//二元擦破在函数
//left:传入对象属性age的值
//right:传入的第二个参数值
IntBinaryOperator binaryOperator = (left, right) -> left+left;
//获取修改前user的age的值,并将引用age更新为二元函数返回的对象.
REFERENCE.getAndAccumulate(user,2,binaryOperator);
//将引用user中属性值age更新为二元函数返回的值,并获取变更后的age值
REFERENCE.accumulateAndGet(user,2,binaryOperator);
//当前引用对象user属性age与1比较
//相等返回true,并设置引用对象user属性age为user2
//不相等返回false,不做变更
REFERENCE.compareAndSet(user,1,2);
//与compareAndSet一致
REFERENCE.weakCompareAndSet(user,1,2);
}
private static class User {
private String userName;
volatile int age;
User(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}
import java.util.concurrent.atomic.LongAdder;
public class AdderAtomic {
/**
* 生成累加器,起始默认为0
*/
private static LongAdder longAdder = new LongAdder();
public static void main(String[] args) {
//返回统计结果
longAdder.sum();
//返回累计结果并重置累加器
longAdder.sumThenReset();
//累加1
longAdder.increment();
//累减1
longAdder.decrement();
//指定累加值
longAdder.add(2);
//重置累加器
longAdder.reset();
}
}
import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class AbaAtomic {
/**
* 初始化整型标记引用类型
* 操作类型数据类型为Integer
* 初始化值为0
* 初始化版本为1
*/
private static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(0,1);
/**
* 初始化标记布尔型引用类型
* 操作类型数据类型为Integer
* 初始化值为0
* 初始化标记为false
*/
private static AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(0,false);
public static void main(String[] args) {
//返回当前标记数值
System.out.println("getStamp:"+atomicStampedReference.getStamp());
//返回当前引用对象值
System.out.println("getReference:"+atomicStampedReference.getReference());
//返回当前引用对象值,并将标记数值存入传入的整型数据0的未位置.
//传入参数-整型数组,会返回当前版本值
int[] stampNum = new int[1];
System.out.println("get:"+atomicStampedReference.get(stampNum));
System.out.println("stampNum:"+stampNum[0]);
//设置新标记,
//参数顺序-当前引用对象,新标记
//传入对象相等,更新标记,返回true
//不相等返回false
System.out.println("attemptStamp :"+atomicStampedReference.attemptStamp(0,2));
System.out.println("attemptStamp num:"+atomicStampedReference.getStamp());
//新值新标记
//参数顺序-引用对象的值,标记版本值
atomicStampedReference.set(1,2);
System.out.println("set num:"+atomicStampedReference.getStamp());
//比较后替换,参数顺序-当前值,替换的值,当前标记值,新的标记值.
//比较相等替换,返回true
//比较不相等,返回false
System.out.println("compareAndSet:"+atomicStampedReference.compareAndSet(1,2,2,3));
System.out.println("compareAndSet num:"+atomicStampedReference.getStamp());
//同compareAndSet
atomicStampedReference.weakCompareAndSet(2,3,3,4);
System.out.println("weakCompareAndSet num:"+atomicStampedReference.getStamp());
//atomicMarkableReference操作与atomicStampedReference基本相同
//返回布尔型标记值
System.out.println("isMarked:"+atomicMarkableReference.isMarked());
// 输出:
// getStamp:1
// getReference:0
// get:0
// stampNum:1
// attemptStamp :true
// attemptStamp num:2
// set num:2
// compareAndSet:true
// compareAndSet num:3
// weakCompareAndSet num:4
// isMarked:false
}
}
AtomicInteger与volatile修饰符使用情况测试如下,多线程中AtomicInteger保证原子性,volatile无法保证原子性
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicAndVolatie {
/**
* 初始化原子整数类型对象
*/
private static AtomicInteger atomicInteger = new AtomicInteger(0);
/**
* 普通线程整数变量
*/
private static volatile int num;
static {
num = 0;
}
public static void main(String[] args) {
//并发个数
int count = 0;
//线程并发次数
int max = 1000;
while (count < max) {
new Thread(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicInteger.getAndIncrement();
}).start();
count++;
}
count = 0;
while (count < max) {
new Thread(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
}).start();
count++;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("AtomicInteger num=" + atomicInteger.get());
System.out.println("volatile num=" + num);
// 输出:
// AtomicInteger num=1000
// volatile num=993
}
}
当两个以上线程对同一个对象进行原子操作时可能会出现,如下图:
步骤说明:
步骤(1)(2):线程t1读取主内存值为A
步骤(3)(4):线程t2读取主内存值为A
步骤(5)(6):线程t2进行CAS操作,将A改为B
步骤(7)(8):线程t2进行CAS操作,将B改为A
步骤(9)(10):线程t1进行CAS操作,将A改为C
虽然t1原子操作修改成功,但t1的A与t2的ABA只有最终值一致,相关信息已经发生变化,如队列中的前后顺序等。
通过使用AtomicStampedReference类中的Stamp标记可以区分数据版本是否一致,避免ABA问题的出现。