Java—如何从引用中实现位窃取

Java—如何从引用中实现位窃取,java,multithreading,lock-free,pointer-arithmetic,Java,Multithreading,Lock Free,Pointer Arithmetic,当涉及到无锁/无等待数据结构的算法时,一些算法会从指针中窃取2个最低有效位,因为它们没有被使用,并将它们用作状态位(例如,如果节点在逻辑上被删除或其他)。我想,在java中,我只需要使用AtomicStampedReference,而不是偷位。然而,我意识到在java中解决ABA问题的唯一方法是使用AtomicStampedReference来跟踪节点是否被更改 注意:如果你不确定ABA的问题是什么,维基百科提供了一个很好的例子来解释它是如何把事情搞砸的: 注:我之所以说解决ABA问题的唯一方

当涉及到无锁/无等待数据结构的算法时,一些算法会从指针中窃取2个最低有效位,因为它们没有被使用,并将它们用作状态位(例如,如果节点在逻辑上被删除或其他)。我想,在java中,我只需要使用AtomicStampedReference,而不是偷位。然而,我意识到在java中解决ABA问题的唯一方法是使用AtomicStampedReference来跟踪节点是否被更改

注意:如果你不确定ABA的问题是什么,维基百科提供了一个很好的例子来解释它是如何把事情搞砸的:

注:我之所以说解决ABA问题的唯一方法是使用AtomicStampedReference,是基于这篇文章:

既然我不能在原子标记的引用中使用整数来跟踪逻辑删除之类的事情,有没有办法窃取引用本身中未使用的位?我试图通过调用以下命令访问“不安全”包来执行此任务:

import sun.misc.Unsafe;
但当我这样做时,我从Eclipse中得到以下错误:

访问限制:由于对所需库C:\Program Files\Java\jre1.8.0\U 40\lib\rt.jar的限制,无法访问不安全类型

有人有什么想法吗?如果您想知道我想做什么,我将尝试在java中实现一个线程安全的无锁hashmap,作为一个学校项目。我需要使用2个LSB位来区分3种不同的节点类型:数据节点(00)、标记数据节点(01)或数组节点(10)

编辑:
我应该提到,我需要2个状态位在原子引用中。我之所以需要这样做,是因为我将要执行比较和交换操作,如果数据节点(00)被标记(01)或转换为arrayNode(10),我需要比较和交换失败。我最初在AtomicStampedReference中使用了整数,但是我不能再这样做了,因为AtomicStampedReference应该被保留以防止ABA引起的问题。

AFAIK,在Java中没有办法窃取这2位

然而,在Java中编写无锁数据结构时,ABA问题通常通过不重用对象来解决。每次更改原子引用时,都会将其设置为新对象,然后将旧对象扔掉。垃圾收集器保证不会在与旧对象相同的地址创建新对象,直到这样做是安全的,并且不会导致ABA问题


对于节点标记之类的事情,可以将原子引用点指向只包含对节点的引用的中间包装器类。然后使用不同混凝土类型的新包装,以更改标记。(例如,DataNodeWrapper->MarkedNodeWrapper)同样,每次您更改原子引用时,都要扔掉旧的包装,这样就不会给您带来任何ABA痛苦。

好的,所以在Java中不可能从引用中窃取位,但我最终从原子标记的引用中窃取位。我从整数戳记中窃取了2个MSB位用作状态位,并将整数中剩余的30位用作计数器。我可以这样做,因为java允许您对整数执行位操作:

我在java并发hashmap代码中实现并测试了这一点。这解决了ABA问题,并且仍然允许我跟踪引用是指向节点、标记节点还是arraynode

感谢您键入_outcast,建议从原子StampedReference的整数中窃取位

编辑 我想我将发布我编写的函数,使用整数的低30位作为计数器,高2位作为标志。函数以AtomicStampedReference的整数作为输入,输出取决于函数。我希望它能帮助任何可能有类似情况的人

//get the incremented counter without messing up the mask bits
//if already at highest value (2^30 - 1, or 1073741823, reset to 0)
private int custInc(int rawStamp){
    int markedBits = 0xC0000000 & rawStamp;
    if (getUnmarkedStamp(rawStamp) == 1073741823)
        return (0 | markedBits);
    else 
        return ((rawStamp + 1) | markedBits);
}

//get the stamp value without the marked bits
private int getUnmarkedStamp(int rawStamp){
    int stampMask = 0x3FFFFFFF;
    return stampMask & rawStamp;
}


//call this to determine if the AtomicStampedReference is pointing to an array node
//10XXXXX... indicates arrayNode;
//01XXXXX... indicates marked data node
//00XXXXX... indicates a normal data node
private boolean isStampArrayNode(int rawStamp){
    int isArrayNodeMask = 0xC0000000;
    if ((isArrayNodeMask & rawStamp) == 0x80000000)
        return true;
    else 
        return false;               
}

//call this to determine if the AtomicStampedReference is pointing to an marked data node
//10XXXXX... indicates arrayNode;
//01XXXXX... indicates marked data node
//00XXXXX... indicates a normal data node
private boolean isStampMarkedDataNode(int rawStamp){
    int isArrayNodeMask = 0xC0000000;
    if ((isArrayNodeMask & rawStamp) == 0x40000000)
        return true;
    else 
        return false;               
}

//call this to get what the raw stamp value if you are to mark it as a marked data node
//01XXXXX... indicates marked data node.ensure that this is returned
private int getStampMarkedAsDataNode(int rawStamp){
    return (rawStamp | 0x40000000) & 0x7FFFFFFF;
}

//call this to get what the raw stamp value if you are to mark it as an array node
//10XXXXX... indicates arrayNode;
private int getStampMarkedAsArrayNode(int rawStamp){
    return (rawStamp | 0x80000000) & 0xBFFFFFFF;
}

如果你需要两个比特,就用一个。而且,Java没有指针。好吧,问题是我需要位在原子引用中。这样做的原因是因为我要做一个比较和设置,如果数据节点(00)被标记(01)或变成数组节点(10),我希望比较和设置失败。我不认为你能得到你想要的。位窃取假设存在随机的无意义的暂存空间,这在Java的JVM模型或实现级别上都是不准确的。还有,有什么原因可以解释这不是的副本吗?我不明白为什么不能使用
AtomicStampedReference
并在每次节点更改时增加它。如果需要其他特定于数据结构的状态信息,例如节点是否为数据/数组/标记节点,则这些信息可能属于数据结构本身。我想,您可以从AtomicStampedReference中窃取一些位(可能是MSB,所以增量仍然有效),但这似乎不必要地复杂。