CompareAndSwap原子操作原理

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

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

 

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

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

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

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

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

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

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

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

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/386506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STemWin移植笔记

实现将STemWin图形库移植到STM32F103ZET中,记录简单的操作步骤,以便自己后续查阅。 1/ 从官网获取STemWin库的压缩包en.stemwin.zip。 2/ 解压后,在路径STemWin_Library_V1.2.0\Libraries\STemWinLibrary532下可以找到库文件。 3/ 移植前的准…

这些新技术你们都知道吗?看这一篇就够了!

前言 现在已经进入招聘季节,本篇文章旨在分享知名互联网企业面试官面试方法和心得,希望通过本文的阅读能给程序员带来不一样的面试体验和感受,放松面试心态,积极备战! 面试题 PS:由于文章篇幅问题&#x…

spring boot redis 分布式锁

yml文件 redis:host: 127.0.0.1port: 40197password: 123456timeout: 5000database: 0jedis:pool:min-idle: 0max-idle: 8max-active: 8max-wait: -1 RedisConfig.java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer…

mysql函数(二.数字函数)

数字函数 1.ROUND(X) 四舍五入取整数 ROUND(X,D) 四舍五入根据D几,来保留几位小数 (1)四舍五入取整数 select ROUND(1.567); 结果:2 (2)四舍五入根据D2,来保留2位小数 select ROUND(1.567,2)DESC; 结果:1.57 2.CEIL…

这份1307页Android面试全套真题解析,源码+原理+手写框架

前言 前不久,几个朋友聚会,谈到了现在的后辈,我就说起了那个大三就已经拿到网易offer的小学弟。 这个学弟是00后,专升本进入我们学校的。进来后就非常努力,每次上课都是第一个到教室的,每次都是坐第一排&…

CAS的ABA问题描述 AtomicStampReference

CAS的ABA问题描述 在CAS操作的时候,其他线程将当前变量的值从A改成B,又改回A;CAS线程用期望值A与当前变量比较的时候,发现当前变量没有变,于是CAS就将当前变量进行了交换操作,但其实当前变量改变过&#x…

[转]OpenContrail 体系架构文档

OpenContrail 体系架构文档英文原文:http://opencontrail.org/opencontrail-architecture-documentation/ 翻译者:KkBLuE知行合一 其微信号:kkbluepublic, SDNAP.com翻译整理 OpenContrail 体系架构文档 1 概述 1.1 使用案例 1…

这份354页笔记的Android进阶知识+大厂高频面试题,绝对干货

程序员与别的专业有所不同,其他专业都是越老越香,而程序员却是一个例外,因为计算机技术更新太快,而且工作强度很大,因此大部分程序员只会写 3 年代码。3 年后要不晋升做项目经理,要么转行,个别研…

原子性 atomic 类用法

当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i1,A线程更新i1,B线程也更新i1,经过两个线程操作之后可能i不等于3,而是等于2。因为A和B线程在更新变量i的时候…

这是一份用心整理的Android面试总结,聪明人已经收藏了!

前言 本文想分享的是如何准备阿里面试的以及面试过程的所想所得,希望能帮到你。 首先,可能要让你们失望的是,这篇文章不会有大篇幅的面试题答案。如果想要看这方面的内容,可以看我之前的文章。感谢关注 很多人准备面试的时候&a…

git 技能图

---- 转载于:https://www.cnblogs.com/WHWWHW/p/11136606.html

AtomicStampedReference源码分析

之前的文章已经介绍过CAS的操作原理,它虽然能够保证数据的原子性,但还是会有一个ABA的问题。 那么什么是ABA的问题呢?假设有一个共享变量“num”,有个线程A在第一次进行修改的时候把num的值修改成了33。修改成功之后,紧接着又立刻…

django:bootstrap table加载django返回的数据

bootstrap table加载表格数据有两类方式: 一种通过data属性的方式配置,一种是javascipt方式配置 这里看js配置方式: 1、当数据源为.json文件时 url参数写上json文件的地址就行,但是json文件格式必须为json格式(2种): a:一种为json…

这是一份面向Android开发者的复习指南,成功入职字节跳动

前言 19年6月份从网易云音乐离开,放弃了留学机会,开始了人生的第一次创业,前后尝试了两个项目,因为个人能力与时机因素都失败了,虽然没能享受到创业所能够带来高杠杆物质上的回报,但是对个人软技能和自我边…

JVM启动参数

不管是YGC还是Full GC,GC过程中都会对导致程序运行中中断,正确的选择不同的GC策略,调整JVM、GC的参数,可以极大的减少由于GC工作,而导致的程序运行中断方面的问题,进而适当的提高Java程序的工作效率。但是调整GC是以个极为复杂的过程&#xf…

【UOJ 92】有向图的强连通分量

【题目描述】: 有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G…

这篇文章可以满足你80%日常工作!一线互联网公司面经总结

前言 最近发现大家都喜欢看面试相关的文章,我也跟一波风,总结了一下我面试中所遇到的问题总结,分享一下面试中被问的最多的一些问题。 希望对正在找工作的朋友提供一些帮助。 好了话不多说,进入正题。 作为安卓开发者&#xff…

java并发synchronized 锁的膨胀过程(锁的升级过程)深入剖析(1)

我们先来说一下我们为什么需要锁? 因为在并发情况为了保证线程的安全性,是在一个多线程环境下正确性的概念,也就是保证多线程环境下共享的、可修改的状态的正确性(这里的状态指的是程序里的数据),在java程…

MSCRM二次开发实现自动编号功能

功能描述:对客户实体实现自动编号功能,1、2、3、4...... 自动编号存放于属性accountnumber.原  理:在mscrm服务器用一个文本文件存放当前最新编号,每当创建客户记录时在PreCreate事件接口做以下步骤:1、锁定文本文件…

这篇文章可以满足你80%日常工作!成功入职腾讯

什么是中年危机 根据权威数据显示,国内IT程序员鼎盛时期是在25-27岁左右,30岁对于程序员而言完全是一个38线,接着就是转业转岗的事情,这一点在业界也算是一个共识了。 大学毕业步入IT行业普遍年龄也是在22岁左右,然而…