zoukankan      html  css  js  c++  java
  • 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:      }

    参考文档:

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

  • 相关阅读:
    在vs code中进行本地调试和开启本地服务器
    按highcharts中column形式转对象展现格式
    forEach、for in 、 for of三者的区别
    一位数左边补0,slice也可以
    js判断对象是否为空
    谷歌最佳实践
    谷歌最佳实践
    谷歌最佳实践
    谷歌最佳实践
    企业代码版本管理之争:TrunkBased vs GitFlow vs AoneFlow vs OneFlow vs ExeFlow
  • 原文地址:https://www.cnblogs.com/scy251147/p/11251964.html
Copyright © 2011-2022 走看看