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 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 count=new AtomicStampedReference(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("只执行一次");
}
}
}
//只执行一次