在翻阅AQS(AbstractQueuedSynchronizer)类的过程中,发现其进行原子操作的时候采用的是CAS。涉及的代码如下:

  1. 1: private static final Unsafe unsafe = Unsafe.getUnsafe();
  1. 2: private static final long stateOffset;
  1. 3: private static final long headOffset;
  1. 4: private static final long tailOffset;
  1. 5: private static final long waitStatusOffset;
  1. 6: private static final long nextOffset;
  1. 7:  
  1. 8: static {
  1. 9: try {
  1. 10: stateOffset = unsafe.objectFieldOffset
  1. 11: (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
  1. 12: headOffset = unsafe.objectFieldOffset
  1. 13: (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
  1. 14: tailOffset = unsafe.objectFieldOffset
  1. 15: (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
  1. 16: waitStatusOffset = unsafe.objectFieldOffset
  1. 17: (Node.class.getDeclaredField("waitStatus"));
  1. 18: nextOffset = unsafe.objectFieldOffset
  1. 19: (Node.class.getDeclaredField("next"));
  1. 20:  
  1. 21: } catch (Exception ex) { throw new Error(ex); }
  1. 22: }
  1. 23:  
  1. 24: /**
  1. 25: * CAS head field. Used only by enq.
  1. 26: */
  1. 27: private final boolean compareAndSetHead(Node update) {
  1. 28: return unsafe.compareAndSwapObject(this, headOffset, null, update);
  1. 29: }
  1. 30:  
  1. 31: /**
  1. 32: * CAS tail field. Used only by enq.
  1. 33: */
  1. 34: private final boolean compareAndSetTail(Node expect, Node update) {
  1. 35: return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
  1. 36: }
  1. 37:  
  1. 38: /**
  1. 39: * CAS waitStatus field of a node.
  1. 40: */
  1. 41: private static final boolean compareAndSetWaitStatus(Node node,
  1. 42: int expect,
  1. 43: int update) {
  1. 44: return unsafe.compareAndSwapInt(node, waitStatusOffset,
  1. 45: expect, update);
  1. 46: }
  1. 47:  
  1. 48: /**
  1. 49: * CAS next field of a node.
  1. 50: */
  1. 51: private static final boolean compareAndSetNext(Node node,
  1. 52: Node expect,
  1. 53: Node update) {
  1. 54: return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
  1. 55: }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

可以看到用到了compareAndSwapObject和compareAndSwapInt方法,那么究竟是怎么用其来实现原子操作的呢?

我们以compareAndSwapObject方法为例,其源码大致如下:

  1. 1: UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h))
  1. 2: UnsafeWrapper("Unsafe_CompareAndSwapObject");
  1. 3: oop x = JNIHandles::resolve(x_h); //待更新的新值,也就是UpdateValue
  1. 4: oop e = JNIHandles::resolve(e_h); //期望值,也就是ExpectValue
  1. 5: oop p = JNIHandles::resolve(obj); //待操作对象
  1. 6: HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long(p, offset);//根据操作的对象和其在内存中的offset,计算出内存中具体位置
  1. 7: oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e, true);// 如果操作对象中的值和e期望值一致,则更新存储值为x,反之不更新
  1. 8: jboolean success = (res == e);
  1. 9: if (success) //满足更新条件
  1. 10: update_barrier_set((void*)addr, x); // 更新存储值为x
  1. 11: return success;
  1. 12: UNSAFE_END

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

从上述源码可以看到,compareAndSwapObject方法中的第一个参数和第二个参数,用于确定待操作对象在内存中的具体位置的,然后取出值和第三个参数进行比较,如果相等,则将内存中的值更新为第四个参数的值,同时返回true,表明原子更新操作完毕。反之则不更新内存中的值,同时返回false,表明原子操作失败。

同样的,compareAndSwapInt方法也是相似的道理,第一个,第二个参数用来确定当前操作对象在内存中的存储值,然后和第三个expect value比较,如果相等,则将内存值更新为第四个updaet value值。

由于原始的方法使用比较麻烦,所以在AQS中进行了封装,大大简化了操作:

  1. 1: private static final Unsafe unsafe = Unsafe.getUnsafe();
  1. 2: private static final long stateOffset;
  1. 3: private static final long headOffset;
  1. 4: private static final long tailOffset;
  1. 5: private static final long waitStatusOffset;
  1. 6: private static final long nextOffset;
  1. 7:  
  1. 8: static {
  1. 9: try {
  1. 10: stateOffset = unsafe.objectFieldOffset
  1. 11: (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
  1. 12: headOffset = unsafe.objectFieldOffset
  1. 13: (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
  1. 14: tailOffset = unsafe.objectFieldOffset
  1. 15: (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
  1. 16: waitStatusOffset = unsafe.objectFieldOffset
  1. 17: (Node.class.getDeclaredField("waitStatus"));
  1. 18: nextOffset = unsafe.objectFieldOffset
  1. 19: (Node.class.getDeclaredField("next"));
  1. 20:  
  1. 21: } catch (Exception ex) { throw new Error(ex); }
  1. 22: }
  1. 23:  
  1. 24: /**
  1. 25: * CAS head field. Used only by enq.
  1. 26: */
  1. 27: private final boolean compareAndSetHead(Node update) {
  1. 28: return unsafe.compareAndSwapObject(this, headOffset, null, update);
  1. 29: }
  1. 30:  
  1. 31: /**
  1. 32: * CAS tail field. Used only by enq.
  1. 33: */
  1. 34: private final boolean compareAndSetTail(Node expect, Node update) {
  1. 35: return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
  1. 36: }
  1. 37:  
  1. 38: /**
  1. 39: * CAS waitStatus field of a node.
  1. 40: */
  1. 41: private static final boolean compareAndSetWaitStatus(Node node,
  1. 42: int expect,
  1. 43: int update) {
  1. 44: return unsafe.compareAndSwapInt(node, waitStatusOffset,
  1. 45: expect, update);
  1. 46: }
  1. 47:  
  1. 48: /**
  1. 49: * CAS next field of a node.
  1. 50: */
  1. 51: private static final boolean compareAndSetNext(Node node,
  1. 52: Node expect,
  1. 53: Node update) {
  1. 54: return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
  1. 55: }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

可以在其他项目中作为小模块进行引入并使用。这样使用起来就非常方便了:

  1. 1:  
  1. 2: /**
  1. 3: * Creates and enqueues node for current thread and given mode.
  1. 4: *
  1. 5: * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
  1. 6: * @return the new node
  1. 7: */
  1. 8: private Node addWaiter(Node mode) {
  1. 9: Node node = new Node(Thread.currentThread(), mode);
  1. 10: // Try the fast path of enq; backup to full enq on failure
  1. 11: Node pred = tail;
  1. 12: if (pred != null) {
  1. 13: node.prev = pred;
  1. 14: if (compareAndSetTail(pred, node)) {
  1. 15: pred.next = node;
  1. 16: return node;
  1. 17: }
  1. 18: }
  1. 19: enq(node);
  1. 20: return node;
  1. 21: }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

参考文档:

https://blog.csdn.net/qqqqq1993qqqqq/article/details/75211993

CompareAndSwap原子操作原理的更多相关文章

  1. Atomic原子操作原理剖析

    前言 绝大部分 Objective-C 程序员使用属性时,都不太关注一个特殊的修饰前缀,一般都无脑的使用其非默认缺省的状态,他就是 atomic. @interface PropertyClass @ ...

  2. Linux Kernel CMPXCHG函数分析

    原文地址:http://blog.csdn.net/penngrove/article/details/44175387 最近看到Linux Kernel cmpxchg的代码,对实现很不理解.上网查 ...

  3. 同步(Synchronization)

    多线程应用程序的存在,在运行打开一个潜在的多线程安全的接入资源. 两个线程相同的资源可能会以意想不到的方式改变相互干扰. 例如.一个线程可以覆盖有一个线程改变或使应用程序进入一个潜在的无效的状态未知. ...

  4. java并发机制锁的类型和实现

    synchronized 和 volatile,是最基础的两个锁! volatile是轻量级锁,它在多核处理器开发中保证了共享变量的可见性.即当一个线程修改一个共享变量时,其他线程能够读到这个修改的值 ...

  5. C++程序员面试题目总结(涉及C++基础、多线程多进程、网络编程、数据结构与算法)

     说明:C++程序员面试题目总结(涉及C++基础知识.多线程多进程.TCP/IP网络编程.Linux操作.数据结构与算法) 内容来自作者看过的帖子或者看过的文章,个人整理自互联网,如有侵权,请联系作者 ...

  6. Java并发之原子变量和原子引用与volatile

    我们知道在并发编程中,多个线程共享某个变量或者对象时,必须要进行同步.同步的包含两层作用:1)互斥访问(原子性):2)可见性:也就是多个线程对共享的变量互斥地访问,同时线程对共享变量的修改必须对其他线 ...

  7. Java 理论与实践: 流行的原子——新原子类是 java.util.concurrent 的隐藏精华(转载)

    简介: 在 JDK 5.0 之前,如果不使用本机代码,就不能用 Java 语言编写无等待.无锁定的算法.在 java.util.concurrent 中添加原子变量类之后,这种情况发生了变化.请跟随并 ...

  8. 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记

    一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能 ...

  9. Java多线程并发编程之原子变量与非阻塞同步机制

    1.非阻塞算法 非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 -- 例如比较和交换.非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞 ...

随机推荐

  1. 【RN - 基础】之FlexBox弹性布局

    前言 弹性盒模型(The Flexible Box Module),又叫FlexBox,意为“弹性布局”,旨在通过弹性的方式来对齐和分布容器中内容的空间,使其能适应不同的屏幕,为盒装模型提供最大的灵活 ...

  2. 教你如何提高 PHP 代码的质量

    说实话,在代码质量方面,PHP 的压力非常大.通过阅读本系列文章,您将了解如何提高 PHP 代码的质量. 我们可以将此归咎于许多原因,但这肯定不仅仅是因为 PHP 生态系统缺乏适当的测试工具.在本文中 ...

  3. nginx项目部署

    1.概念    1.Django项目(Web应用)    2.web服务        1.nginx        2.Apache            LNMP -> Linux Ngin ...

  4. Netty学习——通过websocket编程实现基于长连接的双攻的通信

    Netty学习(一)基于长连接的双攻的通信,通过websocket编程实现 效果图,客户端和服务器端建立起长连接,客户端发送请求,服务器端响应 但是目前缺少心跳,如果两个建立起来的连接,一个断网之后, ...

  5. RestTemplate常用的get和post带参数请求

    在RestTemplate提供的方法中,有一个参数就是目标URL,参数是跟在后面的一个数量可变参数,但是在这里就有个问题,这个方法怎么知道我传的参数值是对应在目标接口的哪个参数的呢: public & ...

  6. MyCat学习 ------分库分表 随笔

    垂直切分.水平切分 1.垂直分库,解决库中表太多的问题. 2.垂直分表,解决表中列太多的问题.例如 商品表 包含 产地.二维码 .时间.价格.各个列.分为不同的小表. 水平切分, 大数据表拆分为小表 ...

  7. 使用Carthage集成Alamofire

    Carthage相较于Cocoapods有着使用灵活,对目标工程改动小的优势,使得它越来越受欢迎.今天就对我使用Carthage集成FBSDK做一个记录. 1.首先https://github.com ...

  8. 第4节:Java基础 - 必知必会(中)

    第4节:Java基础 - 必知必会(中) 本小节是Java基础篇章的第二小节,主要讲述抽象类与接口的区别,注解以及反射等知识点. 一.抽象类和接口有什么区别 抽象类和接口的主要区别可以总结如下: 抽象 ...

  9. CoderForces Round60-(1117A,1117B,1117C题解)

    A. Best Subsegment time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  10. .net core 3.1简体中文语言包,英文只能提示变成中文!

    .net core 3.1简体中文语言包,英文只能提示变成中文!这个是我自己手动用谷歌翻译翻译的一批文档,已经把.net core3.1完全翻译了,由于是翻译器翻译,所以怕翻译有错漏,所以没有去掉英文 ...