在翻阅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:      }

.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:  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

.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:    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:      }

.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:   
   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:      }

.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

最新文章

  1. 用css画图标
  2. 【poj3017】 Cut the Sequence
  3. zoj 3471(状态压缩)
  4. [转]Android 超高仿微信图片选择器 图片该这么加载
  5. 重新想象 Windows 8.1 Store Apps (74) - 新增控件: Flyout, MenuFlyout, SettingsFlyout
  6. hdu 1733 分层网络流 ****
  7. C语言字符串比较(转)
  8. hduoj------2594 Simpsons’ Hidden Talents
  9. idea快捷键(转)
  10. 【转】Linux下(C/C++)使用system()函数一定要谨慎
  11. 重新开始学习javase_对象的摧毁
  12. java03变量和基本数据类型
  13. QT5-控件-QLineEdit-文本输入控件,用来输入密码什么的还不错,可以和Linux登录一样不移动光标哦
  14. ASP.NET 表单认证与角色授权
  15. Win7窗口操作
  16. Linux下hosts、host.conf、resolv.conf
  17. Wireshark网络抓包(二)——过滤器
  18. Html5浏览器支持
  19. Python数据类型-布尔/数字/字符串/列表/元组/字典/集合
  20. PHP XML简介

热门文章

  1. Vue组件通信之非父子组件传值
  2. 极化码之tal-vardy算法(1)
  3. FBV与CBV 及CBV源码分析
  4. 2019-2020-9 20199317 《Linux内核原理与分析》第九周作业
  5. Sql like模糊查询 区分大小写
  6. git 使用详解(2)——安装+配置+获取帮助
  7. Java修炼——暂停线程的四个方法
  8. openlayers6结合geoserver实现地图空间查询(附源码下载)
  9. ZOJ-1709
  10. ARTS-S c++调用pytorch接口