通常使用volatile关键字修饰字段可以实现多个线程的可见性和读写的原子性,但是对于字段的复杂性操作就需要使用synchronize关键字来进行,例如:
public class Counter { private volatile int count = 0; public synchronized int getAndIncr() { return this.count ++; } }
这里可以看到,对于字段的简单设置和获取,volatile可以应付,但是我们想每次获取后自增加1,这样的操作就只能交给synchronize来做,这样做虽然可以但是性能较低,所以我们这里介绍一种新的无锁操作CAS。
什么是CAS(Compare And Swap)
比较并且交换内存地址的数据,由CPU在指令级别上进行操作的原子性。
CAS包含三个参数:1、变量所在的地址A 2、变量应该的值V1 3、我们需要改的值V2。
如果说变量地址A上的值为V1,就用V2进行复制;如果值不是V1,则什么都不操作,返回V1的值。
自旋操作:在一个死循环里不停的进行CAS的操作,直到成功为止。
CAS实现原子操作的三大问题
- 当线程想把地址A的值V1改为V3时,当线程取到V1的值后再进行比较时,地址A的值从V1->V2->V1进行了改变,虽然V3可以正常赋值,但是比较的V1值已经不是取出来的V1了。
- 循环时间很长的话,CPU的负荷较大。
- 对一个变量进行操作可以,同时操作多个共享的变量有些麻烦。
CAS线程安全
通过硬件层面的阻塞实现原子操作的安全性。
常用的原子操作类
- 更新基本类型类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
- 更新数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
- 更新引用类:AtomicReference,AtomicReferenceFieldUpdater,AtomicMarkableReference
- 原子更新字段类:AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicStampedReference
public class AtomicArray { static int[] value = new int[]{1, 2}; static AtomicIntegerArray aia = new AtomicIntegerArray(value); public static void main(String[] args) { aia.set(0, 3); System.out.println(aia.get(0)); System.out.println(value[0]); } }
输出:
3 1
可以看出AtomicIntegerArray对象将int数组复制了一份,他的改变并没有影响到原有数组。
public class AtomicRef { static class User { private String name; private int age; public User(String name, int age) { 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; } } static AtomicReference<User> userAtomicRef = new AtomicReference<AtomicRef.User>(); public static void main(String[] args) { User user = new User("Mark", 25); userAtomicRef.set(user); User updUser = new User("Mark", 28); userAtomicRef.compareAndSet(user, updUser); System.out.println(userAtomicRef.get().getName()); System.out.println(userAtomicRef.get().getAge()); } }
输出:
Mark 28
这里会首先进行比较,然后更新不一样的字段。
这里对CAS三大问题的第一个问题进行解决:
AtomicMarkableReference这个类会在数据被修改之后,标记位会由true变为false,判断这个标记位就可得知。
AtomicStampedReference这个类会在数据上添加时间戳,如果有数据修改时间戳会变掉,从而判断数据是否被修改。
本文索引关键字:
CAS:http://www.cnblogs.com/huanStephen/p/8213678.html#CAS
欢迎大家索引!