java-从引用中窃取位

java-从引用中窃取位,java,c,java.util.concurrent,concurrent-programming,compare-and-swap,Java,C,Java.util.concurrent,Concurrent Programming,Compare And Swap,如何从地址中窃取2个MSB以执行原子操作?我只想做一个单词 一个例子 public class Node { long key; long value; Node lchild; // format is flag1,flag2,address Node rchild; // format is flag1,flag2,address } public void createNode() { Node n1 = new Node(); //this sh

如何从地址中窃取2个MSB以执行原子操作?我只想做一个单词

一个例子

public class Node
{
    long key;
    long value;
    Node lchild; // format is flag1,flag2,address
    Node rchild; // format is flag1,flag2,address
}

public void createNode()
{
    Node n1 = new Node(); //this should create a node with format 0,0,address1
}

public void setFlag1(Node n1)
{
    Now the new address should be in format 1,0,address1
}

public void setFlag2(Node n1)
{
    Now the new address should be in format 0,1,address1
}
如果我只需要一个额外的标志,就可以使用原子引用。
AtomicStampedReference
可以使用,但效率不高,因为它会创建一个包含时间戳和引用的额外框

中讨论了C语言中的类似问题

我能给你的关于使用Java的最好建议是在数组索引上而不是地址上做些琐事,因为Java不公开地址。

你可能可以使用sun.misc.Unsafe实现这一点

除此之外,它还有许多
compareAndSwap
方法,可以处理任何对象中的任意二进制数据

话虽如此,如果您正在实现一个二进制搜索树,我建议您查看immutable。这些方法的优点包括:

  • 根据定义,它们是线程安全的,因为它们具有不变性
  • 它们不需要锁
  • 一旦你开始做更复杂的事情(如拍摄子树),进行结构共享的能力将是一个巨大的性能胜利——基本上你不需要进行防御性复制

    • 如果不实现自己的JVM,这是不可能的,JVM支持此类操作并正确处理标志位,例如在执行GC时(此时需要识别所有引用,移动收集器需要更改它们)。即使如此,这也违背了Java的设计原则,Java不包括显式解引用或指针算法(我会计算引用中的变化位,并屏蔽它们以进行解引用)

      相反,我建议您创建标志和节点的新复合边类型:

      public class Node {
          long key;
          long value;
          Child lchild; // child = node reference + flags
          Child rchild;
      }
      
      // this is the edge type which should also be used to reference the root node with flags
      public class Child {
          Node child;
          BitSet flags;
      
          public Child(Node node) {
              this.child = node;
              this.flags = new BitSet(2); // we initialize the flags with 00
          }
          public void setFlag(int index) {
              this.flags.set(index); // index would be 0 or 1 here for the two flags
          }
          public boolean isFlagSet(int index) {
              return this.flags.get(index); // index would be 0 or 1 here for the two flags
          }
      }
      

      复制Maurice Herlihy和Nir Shavit的著作《多处理器编程的艺术》第215页的摘录:

      如Pragma 9.8.1中详细描述的,原子标记参考 对象封装了对类型为T的对象的引用和 布尔标记。这些字段可以一起进行原子更新 或者单独。我们将每个节点的下一个字段设为 原子标记引用。线程A逻辑上通过 在节点的下一个字段中设置标记位,并共享物理 删除时,其他线程执行add()或remove():每个 线程遍历列表,它通过物理方式清理列表 正在删除(使用compareAndSet())它遇到的任何标记节点。在里面 换句话说,执行add()和remove()的线程不会遍历 标记的节点,它们将在继续之前删除这些节点。包含() 方法与LazyList算法中的方法相同,遍历所有 节点,并测试项目是否在 根据其键和标记列出


      要从那些可用于对象引用变量的变量中提取位,需要创建自己的JVM

      您首先必须确保这些位没有被实际使用(例如,当JVM总是在16字节边界上对齐对象时,在引用中采用低阶位)。但有些JVM使用32位引用的所有位

      接下来,您必须在每次加载引用时注入代码,以便在访问关联对象之前清除这些位


      然后,您必须对垃圾收集器执行同样的操作。

      您不允许从引用中窃取位,但有一种方法可以解决此问题: 您可以使用AtomicStampedReference,它允许您进行比较和交换,以原子方式更新引用和整数。可以使用整数作为状态,也可以根据需要从整数的MSB中窃取位。您可以在java中对整数执行位操作,这非常酷:


      最后,我编写了一个java程序,从AtomicStampedReference的整数中窃取了2位,并将其用作状态位,然后将整数的剩余30位用作计数器,以防止ABA问题。

      如果指针是字对齐的,则您希望窃取两个LSB,而不是两个MSB。大多数指针都是单词对齐的。但是,我不知道如何在Java中获得指针的位表示。为什么
      Java.util.concurrent.AtomicReference
      对您不起作用?当我调用
      Node n1=new Node()
      时,操作系统是否总是分配一个与单词对齐的空间?我试图偷取MSB以避免ABA问题,这可能发生在
      compareAndSwap
      上。另外,你能解释一下为什么从LSB偷东西更有效吗?@JimGarrison:我希望我能使用原子参考。但我需要偷两块。我认为原子引用只使用了一个额外的位。如果我错了,请纠正我。除非您编写一些
      本地
      代码,否则无法访问引用的位。您所说的复合边类型是什么意思。你能详细解释一下吗?@arunmoezhi我已经添加了一个代码示例来说明这个建议。SchmeiBer谢谢你的示例。因此,当我用这种结构在二元搜索树中创建一个新节点时,我应该使用
      new node()
      还是
      new Child()
      @arunmoezhi
      new Child(theNode)
      将是正确的,因为
      theNode
      是应该集成在树中的节点-这里可以改进类的命名,但是我想继续讨论这个问题,这个
      不安全的
      包和java中的标准
      并发
      包有什么区别?为什么您认为使用
      unsafe
      包中的CAS可以解决我的问题?基本上,unsafe允许您在不遵循Java常用类型安全规则的情况下操作对象中的数据。这是有风险的,而且很容易导致一些非常讨厌的bug——但从另一方面来说,您可以获得一些出色的性能。通常我会建议不要这样做。