文档章节

Java线程之同步替代方案CAS

士别三日
 士别三日
发布于 06/09 14:56
字数 1357
阅读 7
收藏 0

1.并发系统的同步机制

在设计并发系统的时候,设计同步机制是极为重要的一环。

同步机制就是保证状态安全,保证状态的原子性、可见性和有序性。

最常见的同步机制是使用悲观锁。即使用synchronized、ReentrantLock等工具来实现同步。在这种方案下,如果发生了竞争,一些线程就要被操作系统挂起,在稍后又要恢复运行。操作系统在挂起和恢复线程的过程中存在很大的开销。

与锁相比,volatile是一种更轻量级的同步机制。但是volatile的使用场景局限性很大:它不能用于构建原子的复合操作,就是只能保证一个状态变量同步;它也不能用于新值依赖旧值的情况。

除了这两种情况,还有第三种方案:Compare and Swap,简称CAS。CPU支持很多原子指令,比如:测试并设置(Test-and-Set),获取并递增(Fetch-and-Increment),交换(Swap),比较并交换(Compare-and-Swap)…… CAS正是CPU的一个原子指令。但是,并不是每一种CPU架构都实现了CAS。

CAS包含了3个操作数——需要读写的内存位置V,进行比较的值A和拟写入的新值B。当且仅当V的值等于A时,CAS才会通过原子的方式用新值B来更新V的值。可以把CAS看做一把乐观锁。CAS的典型使用模式是:首先从V中读取值A,并根据A计算新值B,然后再通过CAS以原子方式将V中的值由A变成B。

而且,CAS的性能会随着CPU数量的不同而不同。因为CPU之间要进行同步,所以CPU越多,CAS性能越差。

CAS可能会存在ABA问题,如果V的值首先由A变成B,再由B变成A,这仍然发生了变化,但是可以更新成功。这个问题可以引入版本号来解决:每修改一次,内存的版本号就更新一次,比价的时候带上版本号即可。

当多个线程使用CAS同时更新一组状态变量时,只有一个线程能更新变量的值,而其他线程都将失败。失败的线程不会被挂起,而是返回失败结果,让应用程序开发者处理。开发者可以重试,也可以进行一些恢复操作,也可以直接放弃……这样其实有利有弊:

  • 利:CAS是CPU指令,效率肯定比操作系统层面的锁机制高。然后CAS不需要挂起线程,所以就没有挂起和恢复线程的开销。获取一把无竞争的锁的开销大约是CAS操作的开销的两倍。
  • 弊:竞争失败的善后操作由开发者提供,会增加程序的复杂度;在竞争很激烈的情况下,善后操作会变得很多,直接降低性能。

所以,在竞争程度较高的情况下锁的性能反而可能会更好。而且使用CAS开发的复杂度会比使用锁高很多,建议直接使用由开发专家开发好的基于CAS的并发系统,应用开发者不要随便使用CAS进行开发。

2.Java中用CAS实现的非阻塞算法

从Java 8之后,ConcurrentHashMap、ConcurrentLinkedDeque、ConcurrentLinkedQueue、ConcurrentSkipListMap、ConcurrentSkipListSet内部都使用了CAS实现了非阻塞算法。以ConcurrentHashMap为例,在Java 8之前是使用分段锁的方法实现的并发,Java 8改成了使用CAS。这些类都是由开发专家实现的,我们当然可以放心用,而且性能肯定不错。

3.原子变量类

CAS是CPU指令,所以如果Java平台不做支持,Java开发者是没办法使用CAS的。从Java 5开始,Java平台提供了原子变量类,在int、long和对象的引用等类型上都公开了CAS操作。这样,让我们Java开发者也能一定程度上使用CAS设计并发系统。但是不是所有的CPU都支持CAS指令,这时JVM将使用自旋锁。

共有12个原子变量,可分为4组:标量类、更新器类、数组类以及复合变量类:

  • 标量类:AtomicBoolean、AtomicInteger、AtomicLong。其他基本类型可以先转化成int或long,再使用原子变量类。
  • 更新器类:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater。基于反射对非原子类进行原子更新。
  • 数组类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。可以对数组里面的元素进行原子操作。
  • 复合变量:AtomicReference、AtomicMarkableReference、AtomicStampedReference。对普通对象进行原子操作。后面两个可以防止ABA问题。

总结:

  • CAS适合于竞争程度不高的场合(事实上大部分并发系统都是为了实现业务,竞争都不会很高,不会故意引入大量竞争);
  • CAS设计的系统复杂度比锁实现的系统高,所以Java开发者不要使用CAS开发太复杂的并发系统。在 Java平台中很多非阻塞算法都由CAS实现,Java开发者可以直接使用;
  • CAS是CPU指令,Java开发者不能直接使用,可以借助Java平台公开的原子变量类使用。

© 著作权归作者所有

共有 人打赏支持
士别三日

士别三日

粉丝 38
博文 30
码字总数 43081
作品 0
深圳
程序员
私信 提问
Java轻量级锁原理详解(Lightweight Locking)

大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意。 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操作系统互斥(mutex)来实现...

serenity
2015/08/11
0
0
学习笔记三:Synchronized实现原理与应用

1、Synchronized实现同步基础 java中的每一个对象都可以作为锁,根据Synchronized用的位置可以有这些使用场景: 对于普通同步方法,锁是当前实例对象。 对于静态同步方法,锁是当前类的class...

刘祖鹏
07/12
0
0
Java并发编程学习五:Synchronized的锁优化以及CAS

从前几章的学习当中,我们知道了volidate只能保证可见性以及部分的原子性,而针对大部分的并发场景而言,部分的原子性是满足不了项目需求的,因此使用了锁机制或者原子类操作来满足我们的开发...

JerryLin123
11/22
0
0
CAS原理 Java SE1.6中的Synchronized

在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁(后面的章节还会谈到锁)。 锁机制存在以下问题: (1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延...

指尖的舞者
2014/04/23
0
0
原子变量, 无锁定且无等待算法

Java 理论与实践: 流行的原子 新原子类是 java.util.concurrent 的隐藏精华 public class SynchronizedCounter { } public class SynchronizedMutex { } public class SimulatedCAS { } publ......

刘小兵2014
2010/12/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

PHP生成CSV之内部换行

当我们使用PHP将采集到的文件内容保存到csv文件时,往往需要将采集内容进行二次过滤处理才能得到需要的内容。比如网页中的换行符,空格符等等。 对于空格等处理起来都比较简单,这里我们单独...

豆花饭烧土豆
13分钟前
0
0
使用 mjml 生成 thymeleaf 邮件框架模板

发邮件算是系统开发的一个基本需求了,不过搞邮件模板实在是件恶心事,估计搞过的同仁都有体会。 得支持多种客户端 支持响应式 疼彻心扉的 outlook 多数客户端只支持 inline 形式的 css 布局...

郁也风
16分钟前
2
0
让哲学照亮我们的人生——读《医务工作者需要学点哲学》有感2600字

让哲学照亮我们的人生——读《医务工作者需要学点哲学》有感2600字: 作者:孙冬梅;以前读韩国前总统朴槿惠的著作《绝望锻炼了我》时,里面有一句话令我印象深刻,她说“在我最困难的时期,...

原创小博客
今天
3
0
JAVA-四元数类

public class Quaternion { private final double x0, x1, x2, x3; // 四元数构造函数 public Quaternion(double x0, double x1, double x2, double x3) { this.x0 = ......

Pulsar-V
今天
17
0
Xshell利用Xftp传输文件,使用pure-ftpd搭建ftp服务

Xftp传输文件 如果已经通过Xshell登录到服务器,此时可以使用快捷键ctrl+alt+f 打开Xftp并展示Xshell当前的目录,之后直接拖拽传输文件即可。 pure-ftpd搭建ftp服务 pure-ftpd要比vsftp简单,...

野雪球
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部