处理器实现原子操作
-
使用总线锁保证原子性
我们知道处理器存在内存与缓存,当线程读取数据时,先会去取缓存中的数据,若没有再去内存中拿取数据,这样就会发生缓存中与内存中的数据不一致,从而导致不正确的结果。
为了得到正确的结果,可以在总线上加锁,也就是在内存与缓存上都加锁,也就是给处理器一个#LOCK信号,这样线程就可以独占锁 -
使用缓存锁保证原子性
锁的粒度减少
Java实现原子操作
- 使用cas操作来实现锁
/**
* 原子操作
*
* @author wangmj
* @since 2018/12/26
*/
public class Counter {
private int i = 0;
private AtomicInteger atomicI = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
final Counter counter = new Counter();
List<Thread> ts = new ArrayList<>();
for (int j = 0; j < 100; j++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int k = 0; k < 10000; k++) {
counter.unsafeCount();
counter.safeCount();
}
}
});
ts.add(t);
}
System.out.println(ts.size());
for (Thread t : ts) {
t.start();
}
//等待所有线程执行完成
for (Thread t : ts) {
t.join();
}
System.out.println(counter.i);
System.out.println(counter.atomicI.get());
}
private void unsafeCount() {
i++;
}
private void safeCount() {
while (true) {
int i = atomicI.get();
boolean compareAndSet = atomicI.compareAndSet(i, ++i);
if (compareAndSet) {
break;
}else {
System.out.println("发生并发访问i="+i);
}
}
}
}
结果如下:
998944
1000000
可以看到没有cas操作的发生了错误
- cas操作三大问题
- ABA问题
当检查值发生多次变化,并且最终变回原结果(A-B-A),则发生ABA问题,此时通常多添加一个版本号来标识(A1-B2-A3),同时比较值与版本号,就可以避免ABA(AtomicStampedReferenc类) - 循环开销大
自旋CAS若长时间不成功,则会造成CPU内存大量消耗 - 只能保证一个共享变量CAS操作
当多个共享操作同时CAS时,可以将多个共享变量组合在一个对象内,并用AtomicReference保证对象的原子性
- ABA问题