当谈到无锁/无等待数据结构的算法时,有些算法会从指针中窃取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引起的问题。
答案 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;
}