文档章节

Java线程之同步替代方案CAS

士别三日
 士别三日
发布于 06/09 14:56
字数 1357
阅读 6
收藏 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 理论与实践: 流行的原子 新原子类是 java.util.concurrent 的隐藏精华 public class SynchronizedCounter { } public class SynchronizedMutex { } public class SimulatedCAS { } publ......

刘小兵2014
2010/12/07
0
0
CAS原理 Java SE1.6中的Synchronized

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

指尖的舞者
2014/04/23
0
0
Java SE1.6中的Synchronized

1 引言 在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了J...

serenity
2015/07/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

6. Python3源码—List对象

6.1. List对象 List对象是“变长对象”。 6.1.1. Python中的创建 Python中List对象最重要的创建方法为PyList_New,如下Python语句最终会调用到PyList_New: test = [1, 2, 3, 4, 5] 6.1.2. ...

Mr_zebra
9分钟前
0
0
nginx屏蔽指定接口(URL)

Step1:需求 web平台上线后,需要屏蔽某个服务接口,但又不想重新上线,可以采用nginx屏蔽指定平台接口的办法 Step2:具体操作 location /dist/views/landing/UNIQUE_BEACON_URL { re...

Linux_Anna
16分钟前
0
0
tomcat高并发配置调优

作者:Joker-pan 原文:https://blog.csdn.net/u011622226/article/details/72510385?utm_source=copy --------------------- tomcat 解压就使用的,配置都没动过,肯定不能支持高并发了; ...

imbiao
35分钟前
0
0
mysql 联结,级联查询总结区分

其实我对 数据库的级联或者联结查询一直都是会用,项目能查询出来自己想要的结果即可。 毕竟SQL使用复杂的查询毕竟比较少,而且不难使用。 至于区分他们,我还真的有点模糊。 在看 《SQL必知...

之渊
51分钟前
1
0
区块链入门教程分享区块链POW证明代码实现demo

兄弟连区块链入门教程分享区块链POW证明代码实现demo 这里强调一下区块链的协议分层 应用层 合约层 激励机制 共识层 网络层 数据层 上 一篇主要实现了区块链的 数据层,数据层主...

兄弟连区块链入门教程
58分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部