精华内容
下载资源
问答
  • CAS(Compare-And-Swap:比较并替换)...一、CAS运算流程CAS算法的流程是:先读取一个预期值(A) → 从内存中读取一个值(V),如果A == V,那么就将新的值(B)给写入内存,如果A != V,那就不做任何操作。二、CAS的优缺点...

    CAS(Compare-And-Swap:比较并替换)

    CAS是英文单词CompareAndSwap的缩写,意思就是:比较并替换。简单来说就是比较之后再看情况是否需要替换。CAS是乐观锁思想的一种实现方式。

    一、CAS运算流程

    CAS算法的流程是:先读取一个预期值(A) → 从内存中读取一个值(V),如果A == V,那么就将新的值(B)给写入内存,如果A != V,那就不做任何操作。

    二、CAS的优缺点

    优点:①可以避免优先级倒置和死锁。②允许更高程度的并行机制

    缺点:①ABA问题:(可能会造成数据丢失)

    比如有三个线程ThreadA、ThreadB、ThreadC,这三个线程都要操作一个值value(value初始值为100)。

    三个线程的目的:

    ThreadA(value = 100 → value = 50);

    ThreadB(value = 100 → value = 50);

    ThreadC(value = 50 → value = 100);

    刚开始线程A和B都开始运行,两个线程读取到的预期值都是100,此时线程A将100给改为50,之后C线程突然出现,将50给改成了100,然后线程B被插队了就很不爽,准备去修改值,但是读取到了内存值是100 == 预期值100,于是就把100给修改成了50。可是线程B很纳闷,前两个线程到底搞什么飞机,咋啥也没干,他们是没操作成功还是根本没修改这个值?这内存中的值还是100,我还以为我会修改失败呢。

    所以,线程B纳闷的点就是CAS的缺点,它只能去判断内存值和预期值是否相等来得出这个数是否被别人修改过,但是却不知道,读取了预期值100之后,中间被插了无数队,轮到我去修改时,发现内存值等于预期值就以为前面的线程都没有修改这个数,然后线程B就把值给修改了。然而线程B修改值却一直以为他操作的数是被插队之前的那个数,所以线程B的修改会忽略掉中间插队那些线程的操作,所以线程B的修改可能会造成数据丢失。

    解决办法:Java提供了一个带有标记的原子类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性

    ②CAS只能保证一个共享变量的原子性操作。

    解决办法:可以把多个变量放进一个对象里来进行CAS操作

    ③循环时间长,CPU的开销大。

    三、模拟CAS算法(只是模拟,并非Java底层的真正实现)

    /**

    * @author 弹弹霹雳

    * @create 2020-11-22-11:43

    */

    //模拟CAS算法

    public class TestCompareAndSwap {

    public static void main(String[] args) {

    CompareAndSwap cas = new CompareAndSwap();

    //创建10个线程

    for(int i = 0 ; i < 10; i++){

    new Thread(new Runnable() {

    @Override

    public void run() {

    //获取预期值

    int expectedValue = cas.getValue();

    //尝试去修改值,并接收修改情况

    boolean flag = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));

    System.out.println(flag);

    }

    }).start();

    }

    }

    }

    class CompareAndSwap{

    //通过volatile保证内存的可见性

    private volatile int value = 0;

    public int getValue(){

    return this.value;

    }

    public synchronized int compareAndSwap(int expectedValue, int newValue){

    //读取内存中的值

    int oldValue = this.value;

    //如果内存中的值和预期值相等,那么我们就修改内存中的值

    if(expectedValue == oldValue){

    this.value = newValue;

    }

    //返回内存值,如果value被修改,返回的是修改前的值,主要用来给compareAndSet判断是否修改成功

    return oldValue;

    }

    public boolean compareAndSet(int expectedValue, int newValue){

    //如果预期值和内存值相等,就返回true否则返回false

    return expectedValue == compareAndSwap(expectedValue, newValue);

    }

    }

    本文分享 CSDN - 弹弹霹雳。

    如有侵权,请联系 support@oschina.cn 删除。

    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    展开全文
  • } } } public final boolean compareAndSet(int expect, int update) { //unsafe会调用java公共组件,再调C++代码再调底层CAS算法,我们不用管,值的修改也交给下面的方法 return unsafe....

    场景模拟

    假设商品有500件库存,进行促销预购,每有一位客户预购,商品预购数加1。

    省略数据库的操作,用i++来模拟数据库操作

    正常JAVA实现

    public class CASTest {
    	public static int numValue;//商品预购数
    
    	public static void main(String[] args) throws InterruptedException {
    		CASTest test = new CASTest();
    		for (int i = 0; i < 500; i++) {
    			new Thread(() -> { // 起500个线程,当成500个客户同时都在预购该商品
    				try {
    					Thread.sleep(30);// 模拟操作延时
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				test.addNum();
    			}).start();
    		}
    		Thread.sleep(3000);// 等上面线程都跑完后再看 商品预购数 的值
    		System.out.println("numValue:" + test.numValue);
    	}
    
    	//模拟是预购数加1操作
    	public void addNum() {
    		numValue++;//模拟数据库操作
    		System.out.println(numValue);
    	}
    
    }
    

    预期结果:最后输出的 numValue 值应该是500
    实际结果:

    在这里插入图片描述
    每次有客户预购后numValue的输出是无序的,结果可能不是500

    原因:numValue++ 时,会从内存中读取数值numValue,然后numValue+1,最后再赋值回numValue。 当并发量大时,多个用户同时读取到相同的numValue值,各自+1,再赋值回numValue,导致numValue原本应该加2或者加3最后却只加了1. 显然这样的写法会导致数据一致性错误

    JAVA加锁实现

    写法与上面相同,只是addNum() 方法加个锁 变成了
    
    	//模拟是预购数加1操作
    	public synchronized void addNum() {
    		numValue++;//模拟数据库操作
    		System.out.println(numValue);
    	}
    

    预期结果:最后输出的 numValue 值应该是500
    实际结果:
    在这里插入图片描述
    每次有客户预购后numValue的输出是有序的,结果是500

    显然加锁可以避免并发操作时出现的数据不一致问题,但是每次只能一个客户去进行预购操作,效率低下并且如果中间有客户操作时出现阻塞后面的客户也会受影响,即使用lock锁,实际情况下也不建议使用

    JAVA使用CAS实现

    CAS算法:CAS是CPU的指令,是属于硬件层次的,底层也用了锁,相当于是硬件层次上的处理并发修改变量的操作算法,有三个操作数,内存地址V ,预期值B(就是你获取的numValue的修改前的值),要替换得到的目标值A(修改后的numValue值)。
    

    CAS指令执行时,比较内存地址V(内存地址的numValue值)与预期值B是否相等,若相等则将A赋给B,否则不赋值,这时候我们可以通过自旋去重新去获取期望值,重新再去进行CAS操作。
    我们只需要知道JAVA怎么去调用和传参,具体的更新操作交给CAS算法
    具体写法可以参考AtomicInteger.class

    import java.lang.reflect.Field;
    import sun.misc.Unsafe;
    
    public class CASTest2 {
    
    	private volatile int numValue;//无阻塞修改的参数
    
    	private static Unsafe unsafe;
    	private static final long valueOffset;//相当于CAS的获取内存地址
    
    	static {
    		try {
    			//初始化 unsafe
    			Field f;
    			f = Unsafe.class.getDeclaredField("theUnsafe");
    			f.setAccessible(true);
    			unsafe = (Unsafe) f.get(null);
    			//初始化 valueOffset
    			valueOffset = unsafe.objectFieldOffset(CASTest2.class.getDeclaredField("numValue"));
    		} catch (Exception ex) {
    			throw new Error(ex);
    		}
    	}
    
    	public static void main(String[] args) throws Exception {
    		CASTest2 test = new CASTest2();
    		for (int i = 0; i < 500; i++) {
    			new Thread(() -> { // 起500个线程,当成500个用户都在修改同一个数据
    				try {
    					Thread.sleep(30);// 模拟操作延时
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				test.addNum();
    				System.out.println(test.numValue);
    
    			}).start();
    
    		}
    		Thread.sleep(3000);// 等上面线程都跑完后再看 numValue 的值
    		System.out.println("numValue:" + test.numValue);
    	}
    
    	public final int get() {
    		return numValue;
    	}
    
    	public final int addNum() {
    		for (;;) {
    			//如果竞争失败,重新获取期望值,重新更新,直到成功。实际情况就是用select 去查库重新获取新的期望值
    			int current = get();//期望值
    			int next = current + 1;//目标值
    			if (compareAndSet(current, next)) {
    				//下面可以加数据库更新操作
    				return next;
    			} else {//当多个客户同时修改 numValue时,只有一个人可以操作成功,失败的人就会进入到下面的分支,重新再去更新操作
    				System.out.println("当current=" + current + " 时有竞争并竞争失败,自旋一次重新自增");
    			}
    		}
    	}
    
    	public final boolean compareAndSet(int expect, int update) {
    		//unsafe会调用java公共组件,再调C++代码再调底层CAS算法,我们不用管,值的修改也交给下面的方法
    		return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    	}
    }
    
    

    预期结果:最后输出的 numValue 值应该是500
    实际结果:
    在这里插入图片描述
    每次有客户预购后numValue的输出是无序的,结果是500,并且也会发现会后竞争失败,重新去更新的操作。虽然也会出现竞争的情况,却可以通过CAS算法,避免了java层面上的加锁

    总结:

    上述被并发修改的参数是int类型的,实际上使用别的类型的参数也是可以操作的。但是这个使用cas算法实现的并发修改参数只能保证一个变量,如果同时修改多个参数的情况下还需要进行代码优化,并且如果并发量过大,会导致自旋的次数大大增加,压力在服务器这边,要根据实际情况去判断是否要采用这种操作。
    
    展开全文
  • CAS:Java 的 Atomic 包使用 CAS 算法来更新数据, 而不需要加锁.并发历史:早期计算机 -- 从头到尾执行一个程序, 资源浪费一个进程, 有多个线程提高资源的利用率, 公平串行与并行的区别:串行: 洗茶具, 打水, 烧水, 等...

    CAS:Java 的 Atomic 包使用 CAS 算法来更新数据, 而不需要加锁.

    并发历史:

    早期计算机 -- 从头到尾执行一个程序, 资源浪费

    一个进程, 有多个线程

    提高资源的利用率, 公平

    串行与并行的区别:

    串行: 洗茶具, 打水, 烧水, 等水开, 冲茶

    并行: 打水, 烧水同时洗茶具, 水开, 冲茶

    好处: 可以缩短整个流程的时间

    摩尔定律: 当价格不变时, 集成电路上可容纳的元器件的数目, 约每隔 18-24 个月便会增加一倍, 性能也将提升一倍. 这一定律揭示了信息技术进步的速度.

    让程序充分利用计算机资源

    加快程序响应速度 (耗时任务, web 服务器)

    简化异步事件的处理

    什么时候适合使用并发编程:

    任务会阻塞线程, 导致之后的代码不能执行: 比如一边从文件中读取, 一边进行大量计算的情况

    务执行时间过长, 可以划分为分工明确的子任务: 比如分段下载

    任务间断性执行: 日志打印

    任务本身需要协作执行: 比如生产者消费者问题

    并发编程的挑战之频繁的上下文切换

    CPU 为线程分配时间片, 时间片非常短 (毫秒级别),CPU 不停的切换线程执行, 在切换前会保存上一个任务的状态, 以便下次切换回这个任务时, 可以再加载这 个任务的状态, 让我们感觉是多个程序同时运行的

    上下文的频繁切换, 会带来一定的性能开销

    如何减少上下文切换的开销?

    无锁并发编程. 多线程竞争锁时, 会引起上下文切换, 所以多线程处理数据时, 可以用一些办法来避免使用锁, 如将数据的 ID 按照 Hash 算法取模分段, 不同的线程处理不同段的数据

    来源: http://www.bubuko.com/infodetail-3173152.html

    展开全文
  • 可以通过使用版本号和 CAS 算法进行实现,本篇博客主要介绍CAS 算法的概念,以及对 CAS 算法的实现原理进行分析。 什么是 CAS 算法 CAS:Compare and Swap,即比较再交换,其算法公式如下: 函数公式:CAS(V,E,N)...

    在介绍 CAS 之前,先来了解下什么是乐观锁。

    乐观锁(Optimistic Lock)是指对于数据冲突保持一种乐观态度,操作数据时不会对数据加锁,只有到数据提交的时候才通过某种机制来验证数据是否存在冲突。

    可以通过使用版本号CAS 算法进行实现,本篇博客主要介绍 CAS 算法的概念,以及对 CAS 算法的实现原理进行分析。

    什么是 CAS 算法

    CAS:Compare and Swap,即比较再交换,其算法公式如下:

    函数公式:CAS(V,E,N)

    CAS 操作需要我们提供一个期望值,当期望值与当前线程的变量值相同时,说明还没有线程修改该值,当前线程就可以进行修改,也就是执行 CAS 操作。

    但如果期望值与当前线程的变量值不符,则说明该值已被其他线程修改,此时不执行更新操作,但可以选择重新读取该变量并尝试再次修改,也可以放弃操作。

    CAS 的实现原理

    对 java.util.concurrent.atomic 包下的原子类 AtomicInteger 中的 compareAndSet 方法进行分析,可以发现最终调用的是 sum.misc.Unsafe 这个类。

        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * @param expect the expected value
         * @param update the new value
         * @return {@code true} if successful. False return indicates that
         * the actual value was not equal to the expected value.
         */
        public final boolean compareAndSet(long expect, long update) {
            return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
        }

    Unsafe 类本身是不安全的,它为了速度,在 Java 的安全标准上做出了一定的妥协。Unsafe 的 CAS 依赖的是 JVM 针对不同的操作系统实现的 Atomic::cmpxchg 函数。

    Atomic::cmpxchg 函数使用了汇编的 CAS 操作,并使用 CPU 硬件提供的 lock 信号保证其原子性的实现。

    CAS 的优缺点

    优点

    CAS 是非阻塞的轻量级乐观锁,通过 CPU 指令实现。在资源竞争不激烈的情况下,synchronized 重量锁会进行比较复杂的加锁、解锁和唤醒操作,而 CAS 不会加锁,性能高。

    缺点

    CPU开销大

    在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量却又一直更新不成功,会给 CPU带来很大压力。

    不能保证代码块的原子性

    CAS 机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用 synchronized 同步锁了。

     除了以上两个缺点,CAS 还可能会出现 ABA 的问题,那么 ABA 问题又是什么呢?

    CAS 中的 ABA 问题

    如果一开始位置 V 得到的旧值是 A ,当进行赋值操作时再次读取发现仍然是 A ,并不能说明变量没有被其它线程改变过,有可能是其它线程将变量改为了 B,后来又改回了 A。

     

    对于 ABA 问题,主要有两种解决方案:

    使用版本号

    在变量前面追加版本号,每次变量更新的时候把版本号加一,也就是说,之前的 A-B-A 就会变成 1A - 2B - 3A。

    使用并发包的原子类

    java.util.concurrent.atomic 包下提供了一个可处理 ABA 问题的原子类 AtomicStampedReference。其 compareAndSet 方法首先会检查当前引用是否等于预期引用,且当前标志是否等于预期标志。

    如果全部相等,则以原子方式将该引用的该标志的值设置为给定的更新值。

    CAS 的使用场景

    • Atomic 开头的实现类

    以 Atomic 开头的实现类的底层都使用了 CAS 算法。

    • 自旋锁

    自旋锁是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程不会阻塞,而是将循环等待,不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。可以看成是一个不断自动重试的乐观锁,它是 CAS 算法的一种锁机制的实现。

    • 令牌桶限流器

    系统以恒定的速度向桶内增加令牌,每次请求前从令牌桶里面获取令牌,只有获取到令牌就才可以进行访问。

    当令牌桶内没有令牌的时候,就会拒绝提供服务。其中的限流器就是通过使用 CAS 算法来维护多线程环境下对令牌的增加和分发的。

    参考了如下博客,非常感谢:

    Java并发编程之CAS算法

    java中的cas - 知乎

    展开全文
  • CAS算法

    2021-10-26 21:39:04
    CAS算法过程 算法涉及到三个操作数: 需要读写的内存位置V 需要进行比较的预期值A 需要写入的新值U CAS算法解析: CAS具体执行时,当且仅当预期值A符合内存地址V中存储的值时,就用新值U替换掉旧值,并写入到...
  • cas算法 简解

    2021-05-21 13:53:01
    文章目录CAS算法1. 什么是cas机制2. CAS题外了解:concurrent包的实现 CAS算法 建议:java开发程序员 使用 java 5 之后提供的 java.util.concurrent.acomic包中的cas类 使用java5+提供的cas特性,而不是自己实现,...
  • 乐观锁:CAS算法

    2021-10-04 23:48:24
    乐观锁:CAS算法 CAS算法理解 对CAS的理解,CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。 ...
  • CAS算法原理及ABA问题

    千次阅读 2020-12-28 21:24:54
    CAS 即Compare And Swap 或Compare And Set , 比较并替换。是乐观锁的一种具体实现方式,通过自旋的方式来实现共享变量的同步机制。 了解JMM的话应该知道,当线程在对某个共享变量进行操作时,包括下面几个步骤: 1. ...
  • CAS算法乐观锁的缺点1 ABA 问题2 循环时间长开销大3 只能保证一个共享变量的原子操作CAS与synchronized的使用情景 数据库锁的分类 按锁的粒度划分,可分为表级锁、行级锁、页级锁 按锁级别划分,可分为共享锁、排...
  • CAS算法中的ABA问题的解决
  • 学习了CAS算法的原理,以及ABA问题的解决。 记录个人理解。 CAS 算法: 全称 Compare And Swap ,比较交换算法。 举个例子说明算法的思路: 有一个变量 int a = 0; 在多线程条件下,每个线程使变量 a 进行自增操作 ...
  • CAS算法 ---转载

    2021-08-16 18:57:51
    CAS算法 一、CAS的定义 一个线程失败或挂起并不会导致其他线程也失败或挂起,那么这种算法就被称为非阻塞算法。而CAS就是一种非阻塞算法实现,也是一种乐观锁技术,它能在不使用锁的情况下实现多线程安全,所以CAS也...
  • 之前浅析过自旋锁(自旋锁浅析),我们知道它的实现原理就是CAS算法。CAS(Compare and Swap)即比较并交换,作为著名的无锁算法,它也是乐观锁的实现方式之一。JDK并发包里也有许多代码中有CAS的身影闪烁其中,鉴于CAS...
  • CAS算法学习

    2021-01-28 09:04:08
    CAS算法学习 概念 CAS就是compareAndSwap的缩写,一句话描述:比较并交换。是一个轻量级的同步机制. CAS算法涉及到3个操作数: 需要读写的内存值V 进行比较的值A 需要更新的值B 怎么使用 以AtomicInteger类进行说明...
  • CAS(Compare-And-Swap) 算法保证数据变量的原子性 * CAS 算法是硬件对于并发操作的支持 * CAS 包含了三个操作数: * ①内存值 V * ②预估值 A * ③更新值 B * 当且仅当 V == A 时, V = B; 否则,不会执行任何...
  • jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。JDK 5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。 2、CAS算法理解...
  • 模拟 CAS 算法

    2021-05-16 22:04:12
    * 模拟 CAS 算法 */ public class TestCompareAndSwap { @Test public void test(String[] args) { final CompareAndSwap cas = new CompareAndSwap(); for (int i = 0; i < 10; i++) { new Thread(new ...
  • CAS算法基于硬件平台的汇编指令,其中需要对乐观锁和悲观锁进行理解 悲观锁:所访问的变量会被其他线程来访问,必须先锁住; 乐观锁:所访问的变量不会被其他线程访问; 乐观锁是非阻塞的,所以不会出现死锁 CAS算法...
  • 一是探究最基础的i++与++i操作原理,二是探究volatile关键字,三是探究CAS算法。而volatile和CAS算法都是JUC线程高级的内容。 一、i++与++i 假设int i =0;那么经过i++和++i两种场景的独立操作之后。i的新值等于...
  • Java并发之原子变量及CAS算法-下篇概述本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK提供的类中式怎么保证变量原子性的呢?。对应Java中的包是:java.util.concurrent.atomic包下。因为涉及到了CAS...
  • } } 23.03 CAS算法 A:CAS算法 CAS(Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而 设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问 CAS是一种无锁的非阻塞算法的实现。...
  • 原标题:Java并发之原子变量及CAS算法-上篇Java并发之原子变量及CAS算法-上篇 编辑概述本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK提供的类中式怎么保证变量原子性的呢?。对应Java中的包是:java...
  • CAS算法 CAS(Compare-And-Swap)是一种硬件对并发的支持,针对多处理器操作而设计的,处理器中的一种特殊指令,用于管理对共享数据的并发访问。 CAS是一种无锁的非阻塞算法实现,是硬件对于并发操作的支持,保证了...
  • 模拟CAS算法

    2021-01-16 13:05:23
    public class TestVolatile { public static void main(String[] args) { final ThreadDemo td=new ThreadDemo(); for (int i = 0; i < 20;... new Thread(new Runnable() { ... int excepted=td.getValue...
  • CAS算法概述CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V...
  • CAS算法的理解与应用

    2021-03-15 17:27:18
    CAS算法的理解与应用什么是CASCAS的开销CAS的应用 什么是CAS CAS:Compare and Swap,即比较再交换。 主要就是三个值的比较:内存值、旧预期值、新预期值 简单来说,就是线程1和线程2同时从内存中拿到了一个变量a=1...
  • JAVASE学习笔记 简单探索CAS算法原理

    千次阅读 2021-02-04 21:59:43
    简单探索CAS算法原理1.CAS算法理解2.以实例说明CAS算法的作用总结 看了标题后,许多朋友会不禁发出疑问,什么是CAS算法?简单来说,CAS为Compare and Swap的意思,即比较并交换的算法。 1.CAS算法理解 JDK1.5增加...
  • CAS算法是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令。CAS用于管理对共享数据的并发访问。java的并发包中,AQS、原子操作类等都是基于CAS实现的。CAS 是一种 无锁的 非阻塞算法的 实现。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,623
精华内容 24,649
关键字:

cas算法