文档章节

非阻塞同步之 CAS

长安一梦
 长安一梦
发布于 06/23 10:16
字数 1353
阅读 17
收藏 0

为解决线程安全问题,互斥同步相当于以时间换空间。多线程情况下,只有一个线程可以访问同步代码。这种同步也叫阻塞同步(Blocking Synchronization).

这种同步属于一种悲观并发策略。认为只要不同步,共享数据就会被并发访问。随着硬件指令集的发展,我们可以采用基于冲突检测的乐观并发策略。

先进行操作,如果没有其他线程操作共享数据,就操作成功;否则采取补偿措施,去重试直到成功。这种策略不需要把线程挂起,因此称为非阻塞同步。(Non-Blocking Synchrinization)

要保证两个操作的原子性,需要借助处理器指令来完成。这类指令有:

  1. 测试并设置(test-and-set)

  2. 获取并增加(fetch-and-increment)

  3. 交换(swap)

  4. 比较并交换(compare-and-swap,简称 CAS)

  5. 加载链接、条件存储(load-linked/store-conditional)

CAS 指令需要有 3 个操作数,分别为内存位置(V,变量的内存地址),旧的预期值(A),和新值(B)。CAS 指令执行时,当且仅当 V 的值复合旧的预期值时,处理器使用 B 更新 V 值。否则不更新,上述操作是一个原子操作。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”这其实和乐观锁的冲突检查+数据更新的原理是一样的。

Java 中的 CAS 操作:

public class CasTest {

    //private Integer count = 0;
    private final AtomicInteger count = new AtomicInteger(0);


    private ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 20,
            10, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(100));

    private void increase() {
        // count++;
        count.incrementAndGet();
    }

    @Test
    public void testMutiThreadAdd() {
        for (int i = 0; i < 5; i++) {
            executor.execute(() -> {
                for (int j = 0; j < 1000; j++) {
                    increase();
                }
            });
        }

        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(count);
    }

}

使用 atomicInteger 后,每次都能输出一致的结果。increamentAndGet( ) 通过 CAS 保证了 自增操作的原子性;

CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作

1.  ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

2. 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

3. 只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作.

参考: 深入理解Java 虚拟机

© 著作权归作者所有

共有 人打赏支持
上一篇: Java 内存模型
长安一梦
粉丝 1
博文 33
码字总数 25581
作品 0
杭州
其他
私信 提问
非阻塞同步算法与CAS(Compare and Swap)无锁算法

锁(lock)的代价 锁是用来做并发最简单的方式,当然其代价也是最高的。内核态的锁的时候需要操作系统进行一次上下文切换,加锁、释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会...

空云万里晴
2016/06/16
63
0
《Java并发编程实战》读书笔记五:深入理解同步实现

一、构建自定义的同步工具 1. 内置的条件队列 条件队列就如同烤面包机上的面包已好的铃声。如果你正在听着它, 当面包烤好后你可以立即注意到, 并且放下手头的事情开始品尝面包, 如果你没有听...

小七奇奇
08/17
0
0
从源码角度彻底理解ReentrantLock(重入锁)

目录 1.前言 2.AbstractQueuedSynchronizer介绍 2.1 AQS是构建同步组件的基础 2.2 AQS的内部结构(ReentrantLock的语境下) 3 非公平模式加锁流程 3.1加锁流程真正意义上的入口 3.2 尝试获取锁...

takumiCX
08/07
0
0
无锁的同步策略——CAS操作详解

1. 从乐观锁和悲观锁谈起 乐观锁和悲观锁是两种不同的解决并发问题的策略。悲观锁策略假定任何一次并发都会发生冲突,所以总是采用最严格的方式来进行并发控制。java中的独占锁(synchronized...

takumiCX
07/14
0
0
volatile原理与技巧--Java

为什么使用volatile比同步代价更低? 同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能. 而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低....

刘小兵2014
2010/12/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Apache日志不记录访问静态文件,访问日志切割,静态元素过期时间设置

Apache配置不记录访问静态文件的日志 网站大多元素为静态文件,如图片、css、js等,这些元素可以不用记录 vhost原始配置 <VirtualHost *:80> ServerAdmin test@163.com DocumentRoo...

野雪球
36分钟前
0
0
聊聊storm的ICommitterTridentSpout

序 本文主要研究一下storm的ICommitterTridentSpout ICommitterTridentSpout storm-core-1.2.2-sources.jar!/org/apache/storm/trident/spout/ICommitterTridentSpout.java public interface......

go4it
41分钟前
1
0
Ubuntu常用操作

查看端口号 netstat -anp |grep 端口号 查看已使用端口情况 netstat -nultp(此处不用加端口号) netstat -anp |grep 82查看82端口的使用情况 查找被占用的端口: netstat -tln netstat -tl...

hc321
昨天
1
0
网站cdn的静态资源突然访问变的缓慢,问题排查流程

1.首先我查看了一下是否自己的网络问题,通过对比其他资源的访问速度和下载速度,确认不是 2.通过ping 和 tracert 判断cdn域名能否正常访问,(最后回想感觉这一步可以省略,因为每次最终能访...

小海bug
昨天
3
0
Mybatis 学习笔记四 MyBatis-Plus插件

Mybatis 学习笔记四 MyBatis-Plus插件 maven依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <ve......

晨猫
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部