通常情况下,共享变量在并发中的处理是使用synchronized关键字或lock,
xxxxxxxxxx// 共享变量static int count = 0;
// 并发方法public static synchronized boolean request() { count++;}但是这种方式效率过低,是否存在一种更高效的处理方式:在修改共享变量之前,判断期望值和当前值,如果不一致则一直等待,如果相等那么修改共享变量。新的方式只需要在判断逻辑中加锁
xxxxxxxxxx// volatile保证可见性,避免拿到缓存volatile static int count = 0;
/*** 判断是否更新* @param expectCount 期望值count* @param newCount 赋新值* @return 是否成功*/public static synchronized boolean compareAndSwap(int expectCount, int newCount) { // 判断当前count和期望值是否一致 if (getCount() == expectCount) { count = newCount; return true; } return false;}
public static int getCount() { return count;}
public static void request(){ int expectCount; // 判断和赋值 while(!compareAndSwap((expectCount=getCount()),expectCount+1)){}}
CAS全称是CompareAndSwap,比较并替换
CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作
CAS是通过JNI借助C语言实现的,例如指令cmpxchg
系统底层进行CAS操作时,会判断当前系统是否是多核心系统,会给总线加锁,然后执行CAS操作
CAS的问题:高并发情况下存在性能问题
如果存在以下的情况: 并发1:获取出数据的初始值是A,后续计划实施CAS,期望数据还是A,新值为B 并发2:将数据修改成B再修改回A 并发1:CAS检测发现被修改后的数据是A,修改为B
或者可以理解为丢失了一个版本更新
简单地模拟
xxxxxxxxxxpublic static AtomicInteger a = new AtomicInteger(1);
public static void main(String[] args) { Thread main = new Thread(new Runnable() { public void run() { System.out.println("操作线程:"+Thread.currentThread().getName()); System.out.println("初始值:"+a.get()); try { int expectNum = a.get(); int newNum = expectNum+1; Thread.sleep(1000); boolean isCASSuccess = a.compareAndSet(expectNum,newNum); System.out.println("操作线程:"+Thread.currentThread().getName()); System.out.println("CAS是否成功:"+isCASSuccess); }catch (Exception e){ e.printStackTrace(); } } },"主线程"); Thread other = new Thread(new Runnable() { public void run() { try { // 确保主线程优先执行 Thread.sleep(20); // a++ a.incrementAndGet(); System.out.println("操作线程:"+Thread.currentThread().getName()); System.out.println("increment:"+a.get()); // a-- a.decrementAndGet(); System.out.println("操作线程:"+Thread.currentThread().getName()); System.out.println("decrement:"+a.get()); }catch (Exception e){ e.printStackTrace(); } } },"干扰线程");
main.start(); other.start();}打印如下,主线程并没有感受到这个值的变化
xxxxxxxxxx操作线程:主线程初始值:1操作线程:干扰线程increment:2操作线程:干扰线程decrement:1操作线程:主线程CAS是否成功:true问题解决:
对上述案例的解决
xxxxxxxxxx// 第一个参数是数据,第二个参数是初始化版本public static AtomicStampedReference<Integer> a = new AtomicStampedReference<>(1, 1);
public static void main(String[] args) { Thread main = new Thread(new Runnable() { public void run() { System.out.println("操作线程:" + Thread.currentThread().getName()); System.out.println("初始值:" + a.getReference()); try { Integer expectReference = a.getReference(); Integer newReference = expectReference + 1;
int expectStamp = a.getStamp(); int newStamp = expectStamp + 1;
Thread.sleep(1000); boolean isCASSuccess = a.compareAndSet(expectReference, newReference, expectStamp, newStamp); System.out.println("操作线程:" + Thread.currentThread().getName()); System.out.println("CAS是否成功:" + isCASSuccess); } catch (Exception e) { e.printStackTrace(); } } }, "主线程"); Thread other = new Thread(new Runnable() { public void run() { try { // 确保主线程优先执行 Thread.sleep(20); // a++ a.compareAndSet(a.getReference(), a.getReference() + 1, a.getStamp(), a.getStamp() + 1); System.out.println("操作线程:" + Thread.currentThread().getName()); System.out.println("increment:" + a.getReference()); // a-- a.compareAndSet(a.getReference(), a.getReference() - 1, a.getStamp(), a.getStamp() + 1); System.out.println("操作线程:" + Thread.currentThread().getName()); System.out.println("decrement:" + a.getReference()); } catch (Exception e) { e.printStackTrace(); } } }, "干扰线程");
main.start(); other.start();}成功解决
xxxxxxxxxx操作线程:主线程初始值:1操作线程:干扰线程increment:2操作线程:干扰线程decrement:1操作线程:主线程CAS是否成功:false
JDK在sun.misc.unsafe类中实现
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);var1:表示要操作的对象(比较并替换的对象) var2:表示要操作对象中属性地址的偏移量 var4:修改数据的期望值 var5:需要修为的新值