java并发编程第十五章笔记

第十五章 原子变量与非阻塞同步机制

一 比较并转换(CAS)

CAS包含了三个操作数:需要读写的内存位置V,进行比较的值A,和拟写入的新值B。当且仅当V的值等于A时,CAS才会通过源自方式用B来更新V的值,否则不会执行任何操作。

乐观锁就用到了此机制。

CAS的典型使用模式: 

首先从V中读取值A,根据A计算值B,然后通过CAS以原子操作将V的值A变为B。

一个模拟的CAS:

/**
 * 模拟CAS操作
 * @author cream
 *
 */
public class SimulatedCAS{
	private int value;
	
	public synchronized int get(){ return value;}
	
	public synchronized int compareAndSwap(int expectedValue,int newValue){
		int oldValue = value;
		if(oldValue==expectedValue)
			value = newValue;
		return oldValue;
	}
	
	public synchronized boolean compareAndSet(int expectedValue,int newValue){
		return (expectedValue == compareAndSwap(expectedValue, newValue));
	}
}

使用CAS实现的线程安全的计数器:

class CASCounter{
	private SimulatedCAS value;
	
	public int getValue(){
		return value.get();
	}
	
	public int increment(){
		int v;
		do{
			v=value.get();
		}
		while(v != value.compareAndSwap(v, v+1));
		return v+1;
	}
}

递增操作采用了标准形式:读取旧的值,根据它计算出新值(+1),并使用CAS来设置这个新值。如果CAS失败,那么该操作将立即重试。

CAS缺点:CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作

ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

二 原子变量

AtomicInteger来研究在没有锁的情况下是如何做到数据正确性的。

private volatile int value;

首先在没有锁的机制下可能需要借助volatile原语,保证线程间的数据是可见的(共享的)。这样才获取变量的值的时候才能直接读取。

public final int get() {
        return value;
}

然后来看看++i是怎么做到的。

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}
在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。


猜你喜欢

转载自blog.csdn.net/llcream/article/details/80050396