上一篇 <<<Volatile的伪共享和重排序
下一篇 >>>Synchronized锁
CAS(Compare and Swap):比较并交换
优势: 非阻塞性,它对死锁问题天生免疫,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销。
1.Cas 是通过硬件指令实现,保证原子性
2.Java是通过unsafe jni技术
3.原子类: AtomicBoolean,AtomicInteger,AtomicLong 等使用 CAS 实现。
CAS(V,E,N)
- V表示要更新的变量(主内存)
- E表示预期值(本地内存)
- N表示新值
操作机制:【(本地内存值==主内存预期值)?新值更新:迭代循环等待(也叫自旋)】
tips: CAS是通过自旋实现的,但不能说CAS就是自旋锁。
CAS底层源码
// 调用方法
atomic.incrementAndGet();
/*底层源码:*/
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
//这里就是循环自旋
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
/*以前的写法: */
public final int incrementAndGet() {
for (;;) {
//获取当前值
int current = get();
//设置期望值
int next = current + 1;
//调用Native方法compareAndSet,执行CAS操作
if (compareAndSet(current, next))
//成功后才会返回期望值,否则无线循环
return next;
}
}
CAS的ABA问题
刚开始读取的时候是A,更新的时候也是A,但中间可能会被改为B过了。
CAS的ABA问题解决办法
方案1:AtomicStampedReference
java并发包中提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性。
/**
* 使用AtomicStampedReference可获取版本号信息
* 通过版本号可解决ABA问题
*/
atomicStampedReference = new AtomicStampedReference(INIT_NUM, 1);
Integer value = (Integer) atomicStampedReference.getReference();
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + " : 当前值为:" + value + " 版本号为:" + stamp);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//(1)第一个参数expectedReference:表示预期值。
//(2)第二个参数newReference:表示要更新的值。
//(3)第三个参数expectedStamp:表示预期的时间戳。
//(4)第四个参数newStamp:表示要更新的时间戳。
if (atomicStampedReference.compareAndSet(value, UPDATE_NUM, 1, stamp + 1)) {
System.out.println(Thread.currentThread().getName() + " : 当前值为:" + atomicStampedReference.getReference() + " 版本号为:" + atomicStampedReference.getStamp());
} else {
System.out.println("版本号不同,更新失败!");
}
方案2:利用原子类手写CAS无锁
/**
* 定义AtomicInteger 修改为1表示该锁已经被使用该 修改为0表示为被使用
*/
private volatile AtomicInteger atomicInteger = new AtomicInteger(0);
private Thread lockCurrentThread;
/**
* 尝试获取锁
*
* @return
*/
public boolean tryLock() {
boolean result = atomicInteger.compareAndSet(0, 1);
if (result) {
lockCurrentThread = Thread.currentThread();
}
return result;
}
/**
* 释放锁
*
* @return
*/
public boolean unLock() {
if (lockCurrentThread != null && lockCurrentThread != Thread.currentThread()) {
return false;
}
return atomicInteger.compareAndSet(1, 0);
}
Java中Unsafe实现cas无锁机制常用方法
- 获取unsafe实例
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
- cas操作
// o csa对象 offset内存偏移量 expected 预期值 x新值
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
- objectFieldOffset获取内存偏移量
//获取内存偏移量
public native long objectFieldOffset(Field f);
- park操作
//park 是挂起当前线程 isAbsolute 为true 代表睡眠一段时自动唤醒 time是纳秒级别
public native void park(boolean isAbsolute, long time);
- unpark操作
//unpark 将此线程唤醒
public native void unpark(Object thread);
相关文章链接:
多线程基础
线程安全与解决方案
锁的深入化
锁的优化
Java内存模型(JMM)
Volatile解决JMM的可见性问题
Volatile的伪共享和重排序
Synchronized锁
Lock锁
AQS同步器
Condition
CountDownLatch同步计数器
Semaphore信号量
CyclicBarrier屏障
线程池
并发队列
Callable与Future模式
Fork/Join框架
Threadlocal
Disruptor框架
如何优化多线程总结
网友评论