问题
How do I steal 2 MSBs from an address to do an atomic operation? I'm trying to do a single word CAS
An example
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
}
AtomicReference
could be used if I needed only one extra flag.
AtomicStampedReference
could be used but it is not efficient as it creates an extra box containing timeStamp and a reference.
A similar problem in C is discussed in stealing bits from a pointer
回答1:
You can probably implement this using sun.misc.Unsafe
- http://www.docjar.com/docs/api/sun/misc/Unsafe.html
Among other things, this has a number of compareAndSwap
methods that can work with arbitrary binary data within any Object.
Having said that, if you are implementing a binary search tree I'd suggest looking at immutable persistent data structures. Advantages of these include:
- They are thread safe by definition because of immutability
- They require no locks
- The ability to do structural sharing will be a big performance win once you start doing more complicated stuff (e.g. snapshotting of subtrees) - basically you avoid the need to take defensive copies.
回答2:
This is impossible without implementing your own JVM which supports this kind of operations and deals with the flag bits properly, e.g. when doing GC (all references need to be identified at this point and moving collectors will need to change them). Even then, this is against the design principles of Java which do not include explicit dereferencing or pointer arithmetic (which I would count changing bits in a reference and masking them for dereferencing towards).
Instead, I would recommend you to create a new composite Edge type of the flags and the Node:
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
}
}
回答3:
Copying an extract from the book "The art of multiprocessor programming" p.215 by Maurice Herlihy and Nir Shavit:
As described in detail in Pragma 9.8.1, an AtomicMarkableReference object encapsulates both a reference to an object of type T and a Boolean mark. These fields can be atomically updated, either together or individually. We make each node’s next field an AtomicMarkableReference. Thread A logically removes currA by setting the mark bit in the node’s next field, and shares the physical removal with other threads performing add() or remove(): as each thread traverses the list, it cleans up the list by physically removing (using compareAndSet()) any marked nodes it encounters. In other words, threads performing add() and remove() do not traverse marked nodes, they remove them before continuing. The contains() method remains the same as in the LazyList algorithm, traversing all nodes whether they are marked or not, and testing if an item is in the list based on its key and mark.
回答4:
The best suggestion I can give you for working in Java would be to do your bit-twiddling on array indices rather than addresses, since Java does not expose addresses.
回答5:
To take bits out of those available for object reference variables will require creating your own JVM.
You will first have to assure that the bits are not actually used (eg, by taking the low-order bits in a reference when the JVM always aligns objects on a 16-byte boundary). But some JVMs use all bits of a 32-bit reference.
Next, you'll have to inject code every time a reference is loaded to clear those bits before accessing the associated object.
Then you'll have to do the same for the garbage collector.
回答6:
You're not allowed to steal bits from a reference, but there's a way around it: you can use the AtomicStampedReference, which allows you to do a compare and swap to atomically update both a reference and an integer. You can use the integer as a status, or you can steal bits from the MSB of the integer if you want. You can do bit operations on integers in java, which is pretty cool:
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
I ended up writing a java program where I stole 2 bits from the integer of the AtomicStampedReference and used them as status bits, and I used the remaining 30 bits of the integer as a counter to prevent the ABA problem.
来源:https://stackoverflow.com/questions/20537011/java-stealing-bits-from-references