目录
1. 利用锁的同步机制
class SafeCounter{
private int count;
//同一时间只有一个线程可以进入add方法
public synchronized void add(){
//复合操作
this.count ++;
}
}
public static void main(String[] args) {
SafeCounter safeCounter = new SafeCounter();
//多个线程都基于safeCounter对象来调用add方法
//只有一个线程可以成功对safeCounter对象加锁
safeCounter.add();
}
以上方法每次只能一个线程进入,效率很低
2.利用CAS原理完成对值的更新操作-非锁实现
class SafeCounterCAS{
private AtomicInteger count = new AtomicInteger();
//同一个时间可以有多个线程进入add方法
public void add(){
//底层通过CAS实现线程安全
count.getAndIncrement();
}
}
public static void main(String[] args) {
SafeCounterCAS safeCounterCAS = new SafeCounterCAS();
//多个线程都基于safeCounter对象来调用add方法
safeCounterCAS.add();
}
3. CAS
CAS涉及三个操作数
- 需要读写的内存值V
- 预期值A
- 需要修改的最新值B
更新一个值,首先读写的内存值V,如果V=A,将V改变B;如果V!=A,操作就失败
比较成功就修改,比较不成功就是失败的;
线程1希望将0修改为1,读取到当前的值为0,比较成功,把0修改为1,线程2将之前读取到的0和现在的1进行比较,比较失败,所以不能交换。
- CAS操作是一种系统原语,属于操作系统硬件提供的功能,由若干条指令组成。在完成上述compare and swap功能过程中,是连续的不允许被中断(如果compare成功了,那么swap也一定会被执行,而不会说,compare成功了,但swap却失败)
- CAS是一种非阻塞式的同步方式,当线程使用CAS更新失败时,并不会进入阻塞状,而是先采取自旋等待,期望再次尝试时能够成功,也就是不成功就一直循环执行直到成功
-
如果长时间不成功,会给CPU带来比较大的执行开销
4. ABA问题
如果一个变量V初次读取的时候是A值, 并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值, 然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的"ABA"问题。
如何解决ABA问题呢?
JDK 1.5以后的 AtomicStampedReference类就提供了解决ABA问题的能力,增加一个版本号维度。每次执行CAS操作,附加再更新一个版本号,并且保证版本号是递增更新的,那么即便A 变成B之后再变回A,版本号也不会变回最初的,以上也是在数据库中增加一个版本号。
总结
CAS带来了一种无锁解决线程同步,冲突问题
2.ABA问题导致了CAS方案的一定缺陷