定义: 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执
行其中的一部分
i++便不是原子操作
public class Counter {
volatile int i = 0;
public int getI() {
return i;
}
public void add() {
i++;
}
}
测试代码
public class Demo1_CounterTest {
public static void main(String[] args) throws InterruptedException {
final Counter ct = new Counter();
for (int i = 0; i < 6; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
ct.add();
}
System.out.println("done...");
}
}).start();
}
Thread.sleep(6000L);
System.out.println(ct.getI());
}
}
预计应该是打印i的值为60000
而实际上
i++ 便存在竞态条件
竞态条件
当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件.换句话说,就是正确的结果要取决于运气.
也就是说,如果两个线程竞争同一资源时,如果对资源的访问顺序敏感, 就称存在竞态条件
示例代码:
public class CounterLock {
volatile int i = 0;
Lock lock = new ReentrantLock();
public void add() {
lock.lock();
i++;
lock.unlock();
}
}
或者
public class CounterSync {
volatile int i = 0;
public synchronized void add() {
i++;
}
}
CAS(Compare and swap) 机制
1.CAS 属于硬件同步原语,处理器提供的内存操作指令,保证原子性。
2.CAS 操作需要两个参数,一个旧值和一个目标值,修改前先比较旧值是否改变,如果没变,将新值赋给变量,否则不做改变。
3.JAVA中sun.misc.Unsafe类提供了CAS机制
Unsafe使用示例
public class CounterUnsafe {
int i = 0;
public int getI() {
return i;
}
//Unsafe可以修改对象的属性、可以修改数组等等
private static Unsafe unsafe = null;
//一个偏移量,代表了要修改的字段
private static long valueOffset;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
// 暴力反射
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
//指定要修改的字段
Field iFiled = CounterUnsafe.class.getDeclaredField("i");
valueOffset = unsafe.objectFieldOffset(iFiled);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public void add() {
while (true) {
if (unsafe.compareAndSwapInt(this, valueOffset, i, i + 1)) {
return;
}
}
}
}
CAS存在的问题
1.不能用于多个变量来实现原子操作
2.循环CAS自旋让所有线程都处于高频运行,争抢CPU执行.如果长时间不成功,则会带来很大的CPU资源消耗
3.ABA问题 (在多线程环境中,使用CAS时,如果一个线程对变量修改2次,第2次修改后的值和第1次修改前的值相同,那么可能就会出现ABA问题
)
J.U.C包内的原子操作封装类
常用的数据类型的原子操作封装类:
AtomicBoolean: 原子更新布尔类型
AtomicInteger:原子更新整型
AtomicLong:原子更新长整型
如果将上面示例代码的int i = 0 改为原子操作封装类
public class CounterAtomic {
AtomicInteger at = new AtomicInteger(0);
public void add(){
// CAS操作进行累加
at.getAndIncrement();
}
public int getValue(){
return at.get();
}
}
数组的原子操作封装类
AtomicIntegerArray:原子更新整型数组里的元素。
AtomicLongArray:原子更新长整型数组里的元素。
AtomicReferenceArray:原子更新引用类型数组里的元素。
更新器
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicLongFieldUpdater:原子更新长整型字段的更新器。
AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
使用示例:
public class Demo_AtomicIntegerFieldUpdater {
// 新建AtomicIntegerFieldUpdater对象,需要指明是哪个类中的哪个字段
private static AtomicIntegerFieldUpdater atom =
AtomicIntegerFieldUpdater.newUpdater(User.class, "id");
public static void main(String[] args) {
User user = new User(100, 100);
atom.addAndGet(user, 50);
System.out.println("atom.addAndGet(user, 50)值变为:" + user);
}
}
class User {
volatile int id;
volatile int age;
public User(int id, int age) {
this.id = id;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
'}';
}
}
原子更新引用类型
AtomicReference:原子更新引用类型。
AtomicStampedReference:原子更新带有版本号的引用类型
AtomicMarkableReference:原子更新带有标记位的引用类型。
原子计数器
DoubleAdder、 LongAdder:计数器增强版,高并发下性能更好
DoubleAccumulator、 LongAccumulator: 是计数器的增强版,可自定义累加规则
使用示例:
public class Demo_LongAccumulator {
public static void main(String[] args) throws InterruptedException {
LongAccumulator accumulator = new LongAccumulator(
(x,y)->{
// 自定义计数规则
System.out.println("x:" + x);
System.out.println("y:" + y);
return x+y;
},
0L);
for (int i = 0; i < 3; i++) {
accumulator.accumulate(1);
}
System.out.println(accumulator.get());
}
}