上期提到过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并发编程 第二期 CAS相关推荐

  1. Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772470 出自[zejian ...

  2. Java并发编程,无锁CAS与Unsafe类及其并发包Atomic

    为什么80%的码农都做不了架构师?>>>    我们曾经详谈过有锁并发的典型代表synchronized关键字,通过该关键字可以控制并发执行过程中有且只有一个线程可以访问共享资源,其 ...

  3. JAVA并发编程: CAS和AQS

    说起JAVA并发编程,就不得不聊聊CAS(Compare And Swap)和AQS了(AbstractQueuedSynchronizer). CAS(Compare And Swap) 什么是CA ...

  4. Java 并发编程CAS、volatile、synchronized原理详解

    CAS(CompareAndSwap) 什么是CAS? 在Java中调用的是Unsafe的如下方法来CAS修改对象int属性的值(借助C来调用CPU底层指令实现的): /*** * @param o ...

  5. java cas机制_java并发编程中的CAS机制,你理解嘛?

    学习Java并发编程,CAS机制都是一个不得不掌握的知识点.这篇文章主要是从出现的原因再到原理进行一个解析.希望对你有所帮助. 一.为什么需要CAS机制? 为什么需要CAS机制呢?我们先从一个错误现象 ...

  6. 【你问我答】第二期:Java并发编程遇到问题了?尽管抛过来吧!

    点击上方"公众号"可以订阅哦 哈喽,朋友们,[你问我答]又来啦! 上期有朋友在<你与Java大牛的距离,只差这24个问题>一文留言表示专家的回答很高大上,小编替专家小骄 ...

  7. Java并发编程71道面试题及答案

    Java并发编程71道面试题及答案 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方 ...

  8. Java并发编程有多难?这几个核心技术你掌握了吗?

    本文主要内容索引 1.Java线程 2.线程模型 3.Java线程池 4.Future(各种Future) 5.Fork/Join框架 6.volatile 7.CAS(原子操作) 8.AQS(并发同 ...

  9. Java并发编程之介绍

    并发编程简介 将串行执行部分编程并发执行,但要考虑上下文切换和资源调度的时间 并发编程的意义及影响多线程的因素 并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最 大限度地并 ...

  10. Java并发编程75道面试题及答案

    1.在java中守护线程和本地线程区别?java中的线程分为两种:守护线程(Daemon)和用户线程(User).任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(bo ...

最新文章

  1. 清华博士接亲被要求现场写代码,新娘:提醒他吃饭的手艺不能忘!
  2. 深度强化学习的现在,将来与未来
  3. 小心as陷阱(c#)
  4. Strange Definition CodeForces - 1471D
  5. 相比学习好的学生,老师最喜欢努力认真学习的学生
  6. 21-特征匹配方法(Brute-Force蛮力匹配)
  7. 浏览器兼容CSS渐进增强 VS 优雅降级如何选择
  8. Silverlight C# 游戏开发:关于精灵for Silverlight容器
  9. Spring Boot拦截器(WebMvcConfigurerAdapter)
  10. php编写一个计算相对路径的函数,php求相对路径的函数
  11. 3dmax su 简单_3DMAX转SU逆天神器!一键转换,无脑操作,你值得拥有!
  12. USRP X310入门
  13. html5学习开发指南
  14. HEVC编码视频格式
  15. android pie mi 3 tab,前沿科技:三星Galaxy Tab S3和Tab A(2017)正在获得Android 9.0 Pie更新
  16. 强主动性的人,如何做事一杠子到底?
  17. 51nod 1238 最小公倍数之和 V3
  18. LVGL (7) 显示对接
  19. Linux基础命令之tar解压缩详解
  20. 2021年中国羊肉市场供需现状、进出口贸易及价格走势分析[图]

热门文章

  1. 灭霸一个响指,开源众包出现在开源中国的社区APP里,一键接单从此无忧。
  2. SpringBoot + vue 解决跨域问题
  3. Qt MDI及其使用方法
  4. 栈(Stack)——后进先出(LIFO)的数据结构(Data Structures)
  5. jar包打开闪退解决办法
  6. 计算机网络常见简答题
  7. java中文件路径的两种写法说明:左斜杠(/)和右斜杠(\)
  8. 雷达图按照权重和排名计算出每项得分,并且按照综合得分排序
  9. linux 股票指南针,Android 利用方向传感器实现 指南针
  10. 微软发布了最新的Sync Framework 2.0 CTP2