AtomicStampReference
解决CAS
的ABA
问题
什么是ABA
ABA问题:指CAS操作的时候,线程将某个变量值由A批改为B,然而又改回了A,其余线程发现A并未扭转,于是CAS将进行值替换操作,实际上该值曾经被扭转过,这与CAS的核心思想是不合乎的
ABA解决方案
每次变量更新的时候,把变量的版本号进行更新,如果某变量被某个线程批改过,那么版本号肯定会递增更新,从而解决ABA问题
AtomicReference 演示ABA问题
package com.keytech.task;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.atomic.AtomicReference;public class AtomicIntegerTest { private static AtomicReference<Integer> count=new AtomicReference<>(10); public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(()->{ boolean b = count.compareAndSet(10, 12); if(b){ System.out.println(Thread.currentThread().getName()+"批改胜利count="+count.get()); } boolean c =count.compareAndSet(12, 10); if(c){ System.out.println(Thread.currentThread().getName()+"批改胜利count="+count.get()); } }); executorService.execute(()->{ boolean b = count.compareAndSet(10, 100); if(b){ System.out.println(Thread.currentThread().getName()+"批改胜利count="+count.get()); } }); executorService.shutdown(); }}//pool-1-thread-1批改胜利count=12//pool-1-thread-1批改胜利count=10//pool-1-thread-2批改胜利count=100
pool-1-thread-1
将count
由10批改成12,又将count
从12改成10。 pool-1-thread-2
将count
从10胜利改成100。呈现了ABA的问题。
AtomicStampedReference
解决ABA
的问题
以计数器的实现为例,计数器通常用来统计在线人数,在线+1,离线-1,是ABA的典型场景。
package com.keytech.task;import java.util.concurrent.atomic.AtomicStampedReference;public class CounterTest { private AtomicStampedReference<Integer> count=new AtomicStampedReference<Integer>(0,0); public int getCount(){ return count.getReference(); } public int increment(){ int[] stamp=new int[1]; while (true){ Integer value = count.get(stamp); int newValue=value+1; boolean b = count.compareAndSet(value, newValue, stamp[0], stamp[0] + 1); if(b){ return newValue; } } } public int decrement(){ int[] stamp=new int[1]; while(true){ Integer value=count.get(stamp); int newValue=value-1; boolean b = count.compareAndSet(value, newValue, stamp[0], stamp[0] + 1); if(b){ return newValue; } } }}
调用计数器
package com.keytech.task;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.atomic.AtomicReference;public class AtomicIntegerTest { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); Semaphore semaphore=new Semaphore(200); CounterTest counterTest=new CounterTest(); for (int i = 0; i < 5000; i++) { executorService.execute(()->{ try{ semaphore.acquire(); counterTest.increment(); semaphore.release(); }catch (Exception e){ e.printStackTrace(); } }); executorService.execute(()->{ try{ semaphore.acquire(); counterTest.decrement(); semaphore.release(); }catch (Exception e){ e.printStackTrace(); } }); } executorService.shutdown(); System.out.println(counterTest.getCount()); }}//输入0
AtomicBoolean
保障高并发下只执行一次
package com.keytech.task;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;import java.util.concurrent.atomic.AtomicBoolean;public class AtomicBooleanTest { private static AtomicBoolean isHappen=new AtomicBoolean(false); public static int clientTotal=5000; public static int threadTotal=200; public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); Semaphore semaphore=new Semaphore(threadTotal); for (int i = 0; i < clientTotal; i++) { executorService.execute(()->{ try { semaphore.acquire(); update(); semaphore.release(); }catch (Exception e){ e.printStackTrace(); } }); } executorService.shutdown(); } private static void update(){ if(isHappen.compareAndSet(false, true)){ System.out.println("只执行一次"); } }}//只执行一次