1:什么是CAS
compare and swap,主要用在并发场景中,是一种思想,是一种实现线程安全的算法。
在并发编程中实现那些不能被打断的交换操作,从而避免在多线程下执行顺序不确定导致错误。
思路:我认为V的值应该是A,如果是则改成B,如果不是A,说明有人修改过,那我就不修改,避免多人同时修改导致出错。
CAS有三个操作数:内存值V、预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才将内存值修改为B,否则什么都不做。最后返回现在的V值。
2:CAS操作
cas/TwoThreadsCompetition.java
package cas;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* 描述模拟CAS操作,等价代码
*/
public class TwoThreadsCompetition implements Runnable {
private volatile int value;
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if (oldValue == expectedValue) {
value = newValue;
}
return oldValue;
}
public static void main(String[] args) throws InterruptedException {
TwoThreadsCompetition r = new TwoThreadsCompetition();
r.value = 0;
//两个线程对r进行修改,只有一个线程会成功。
Thread t1 = new Thread(r,"Thread 1");
Thread t2 = new Thread(r,"Thread 2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(r.value);
}
@Override
public void run() {
compareAndSwap(0, 1);
}
}
线程级别的调试
将断点设置为线程级别。
在debug模式下,程序运行到断点处,用switch thread切换到线程处打断点的代码处。线程1运行完后,main线程继续向下运行到线程2处。
3:ABA问题
ABA问题,可以用版本号解决。
cas/ABADemo.java
package cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* CAS中的 ABA的问题
*/
public class ABADemo {
public static void main(String[] args) {
AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(100, 1);
System.out.println("===AtomicStampedReference效果演示===");
new Thread(()->{
int initStamp = reference.getStamp();
System.out.println(Thread.currentThread().getName()+"第一次版本号"+initStamp);
//CAS操作
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); }
reference.compareAndSet(
100,
111,
reference.getStamp(),
reference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"第二次版本号"+reference.getStamp());
//CAS操作
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); }
reference.compareAndSet(
111,
100,
reference.getStamp(),
reference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"第三次版本号"+reference.getStamp());
}, "ThreadB").start();
new Thread(()->{
int initStamp = reference.getStamp();
System.out.println(Thread.currentThread().getName()+"第一次版本号"+initStamp);
//休眠3秒后 CAS操作
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }
boolean result = reference.compareAndSet(
100,
2020,
initStamp,
initStamp + 1
);
System.out.println("A线程的版本号:"+initStamp);
System.out.println(Thread.currentThread().getName()+"修改操作是否成功"+result+"====="+reference.getReference());
}, "ThreadA").start();
}
private static void demo01() {
AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
System.out.println("===ABA效果演示===");
new Thread(()->{
//CAS操作
atomicReference.compareAndSet(100, 111);
//CAS操作
atomicReference.compareAndSet(111, 100);
}, "ThreadB").start();
new Thread(()->{
//休眠3秒后 CAS操作
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }
System.out.println(atomicReference.compareAndSet(100, 2020)+"==="+atomicReference.get());
}, "ThreadA").start();
}
}