java-从引用中窃取位
如何从地址中窃取2个MSB以执行原子操作?我只想做一个单词 一个例子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
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()
@arunmoezhinew Child(theNode)
将是正确的,因为theNode
是应该集成在树中的节点-这里可以改进类的命名,但是我想继续讨论这个问题,这个不安全的
包和java中的标准并发
包有什么区别?为什么您认为使用unsafe
包中的CAS可以解决我的问题?基本上,unsafe允许您在不遵循Java常用类型安全规则的情况下操作对象中的数据。这是有风险的,而且很容易导致一些非常讨厌的bug——但从另一方面来说,您可以获得一些出色的性能。通常我会建议不要这样做。