Java - 如何从引用

时间:2015-11-18 04:29:34

标签: java multithreading lock-free pointer-arithmetic

当谈到无锁/无等待数据结构的算法时,有些算法会从指针中窃取2个最低有效位,因为它们没有被使用,并将它们用作状态位(就像节点被逻辑删除一样)或者其他的东西)。我认为在java中,我只使用AtomicStampedReference而不是bit stealing。但是,我意识到解决Java中ABA问题的唯一方法是使用AtomicStampedReference来跟踪节点是否被更改。

注意:如果你不确定ABA的问题是什么,维基百科提供了一个很好的例子来解释它被搞砸了多么糟糕: https://en.wikipedia.org/wiki/ABA_problem

注意:我之所以说解决ABA问题的唯一方法是使用AtomicStampedReference基于这篇文章: http://tutorials.jenkov.com/java-util-concurrent/atomicstampedreference.html#atomicstampedreference-and-the-a-b-a-problem

因此,由于我不能使用原子标记引用中的整数来跟踪逻辑删除之类的事情,有没有办法可以窃取引用本身中未使用的位?我试图通过调用:

来访问“unsafe”包来执行此任务
import sun.misc.Unsafe;

但是当我这样做时,我从Eclipse中得到以下错误:

访问限制:由于对所需库C:\ Program Files \ Java \ jre1.8.0_40 \ lib \ rt.jar

的限制,无法访问类型Unsafe

有人有什么想法吗?如果你很好奇我正在尝试做什么,我试图在java中实现一个线程安全锁定免费hashmap作为学校项目。我需要使用2个LSB位来区分3种不同的节点类型:数据节点(00),标记数据节点(01)或阵列节点(10)

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

2 个答案:

答案 0 :(得分:2)

AFAIK,无法用Java窃取这两个位。

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

对于节点标记之类的东西,您可以将原子引用指向一个仅包含对您的节点的引用的中间包装类。然后将CAS转换为具有不同具体类型的新包装,以便更改标记。 (例如,DataNodeWrapper - > MarkedNodeWrapper)同样,每次更改原子引用时,都要抛弃旧的包装,这样就不会引起任何ABA的悲伤。

答案 1 :(得分:-1)

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

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

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

感谢type_outcast提出从AtomicStampedReference的整数中窃取位的建议!

修改 我想我会发布我写的函数,使用整数的低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;
}