上期提到过AtomicInteger ,其中一个赋值方法compareAndSet(),CAS就是原子类比较并设值方法的缩写。
CAS 全程是CompareAndSwap 比较并交换,是一条CPU原语,这个过程是原子的
CAS 简单实用
package com.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author liuxu
* @date 2021/11/10 21:59
*/
public class CasDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(10);
System.out.println(atomicInteger.compareAndSet(10,100)+"当前值是:"+atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(10,100)+"当前值是:"+atomicInteger.get());
}
}
如果和预期值不一致,会导致值修改失败
CAS底层原理可以从
atomicInteger.incrementAndGet();看起
/*this 当前对象
valueOffset 内存偏移量
unsafe unsafe类,cas关键
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
unsafe类下方法
var1 当前对内存偏移量
var2 内存偏移量
var4 增加值
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2); //相当于从主物理内存拿到当前对象物理内存上的值 var5
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
//这个方法在java中看不到实现,可以在java源码包中找到,sun.misc包下可以看到源码
/*
*这个方法首先会将 内存偏移量得到的值var5 和 原来的值var1进行比较 如果相同,说明当前
*线程备份值和内存中的值相同(这里不明白可以看第一期)JMM模型中线程操作内存的方法,可以进行值的增加,
*返回ture,在原来值上增加var4,操作成功,由于方法native修饰,此方法线程安全
*如果失败,只能重新获取内存偏移量上的值,直到设置成功。
*/
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
CAS的缺点
1.如果一个线程一直未CAS设值成功,就会while循环,可能会给CPU带来较大开销
2.会存在ABA问题,如果A线程拷贝了主内存中的值,但是还没有进行值的修改,
此时B线程将主线程中的值由A改成B,然后再改成A,此时如果A线程进行CAS操作,
会认为主线程的值并没有修改
3.可以使用的值比较单一
原子引用可以解决这个问题,首先他可以将对对象封装成原子类
package com.cas;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author liuxu
* @date 2021/11/10 22:42
*/
public class AtomDemo {
public static void main(String[] args) {
AtomicReference<String> reference = new AtomicReference<>();
reference.set("张三");
System.out.println(reference.get());
boolean b = reference.compareAndSet("张三", "zhang");
System.out.println(b+reference.get());
boolean c = reference.compareAndSet("张三", "zhang");
System.out.println(c+reference.get());
}
}
版本号引用,解决ABA问题
package com.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @author liuxu
* @date 2021/11/10 22:51
*/
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(0);//原子引用
static AtomicStampedReference<Integer> stampedReference =
new AtomicStampedReference<>(0,1);//版本号原子引用
public static void main(String[] args) {
new Thread(()->{
atomicReference.compareAndSet(0,100);
atomicReference.compareAndSet(100,0);
},"A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = atomicReference.compareAndSet(0, 100);
System.out.println(Thread.currentThread().getName()+"设置值"+b);
},"B").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("以上说明ABA问题存在,版本号原子引用可以解决");
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
stampedReference.compareAndSet(0,100,stampedReference.getStamp(),stampedReference.getStamp()+1);
stampedReference.compareAndSet(100,0,stampedReference.getStamp(),stampedReference.getStamp()+1);
},"C").start();
new Thread(()->{
int stamp = stampedReference.getStamp();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean andSet = stampedReference.compareAndSet(0, 2009, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName()+"获取的版本号是"+stamp);
System.out.println(Thread.currentThread().getName()+"实际的版本号是"+stampedReference.getStamp());
System.out.println(Thread.currentThread().getName()+"是否修改成功"+andSet);
},"D").start();
}
}
CountDownLatch 用于多线程间递减计数,唤醒另一线程。
一个或者多个线程功来调用 countDownLatch.await(); 如果 countDownLatch不是0就会被阻塞
直到 countDownLatch.countDown();将初始值递减到0
package com.cas;
import java.util.concurrent.CountDownLatch;
/**
* @author liuxu
* @date 2021/11/11 20:20
*/
/**
* CountDownLatch countDownLatch.countDown()递减计数,
* 直到为0 countDownLatch.await();才会放行countDownLatch.await()所在线程
*/
public class CountDownLathDemo {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i <10 ; i++) {
new Thread(()->{
countDownLatch.countDown();
System.out.println("第"+Thread.currentThread().getName()+"个人离开");
},i+"").start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("闭门关灯");
}
}
CyclicBarrier
CyclicBarrier构造时声明最终目标值和要运行的方法,
cyclicBarrier.await();一次,cyclicBarrier从0自增1,达到目标值,lamda表达式中的方法运行
package com.cas;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author liuxu
* @date 2021/11/11 20:50
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("葫芦小金刚出世");
});
for (int i = 0; i < 7; i++) {
new Thread(()->{
System.out.println("葫芦娃老"+Thread.currentThread().getName()+"来了");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},i==0?"大":i+1+"").start();
}
}
}
Semaphore
用于多个共享资源的互斥 用于并发线程数的控制
用于类似抢车位式占用资源
首先new 2个信号量,最多两个人同时用车
semaphore.acquire(); 占用信号量,最多两个线程同时占用
占用后任务执行完毕finally semaphore.release();释放信号量资源
下一个线程可以立马抢占
package com.cas;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @author liuxu
* @date 2021/11/11 21:04
*
*/
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore =new Semaphore(2);
for (int i = 0; i <10 ; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"占用车位后释放");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
},i+"").start();
}
}
}
本文介绍了Java并发编程中的原子类AtomicInteger及其compareAndSet()方法,展示了其基于CPU原语的CAS操作。还讨论了CAS的优缺点,包括可能导致的循环开销和ABA问题,并通过AtomicReference和AtomicStampedReference解决了ABA问题。此外,文章还探讨了CountDownLatch、CyclicBarrier和Semaphore等并发控制工具类的使用场景和工作原理。
2234

被折叠的 条评论
为什么被折叠?



