today I read AQS source code.I found some confuse place about AbstractQueuedSynchronizer#addWaiter
method:
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
does pred
value changed after compareAndSetTail
in second if statement?
so I make a test for it. but it make me more and more confuse.
I found compareAndSetTail
call compareAndSwapObject
finally.
so I test compareAndSwapObject
in Unsafe Package.
this is my test code
private static final long nameOffset;
private String name="init-name";
static Unsafe unsafe = reflectGetUnsafe();
static {
try {
nameOffset = unsafe.objectFieldOffset
(AQSTest.class.getDeclaredField("name"));
} catch (Exception ex) { throw new Error(ex); }
}
private static Unsafe reflectGetUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
return null;
}
}
@Test
public void testUnsafe() {
String preName = name;
unsafe.compareAndSwapObject(this, nameOffset, preName, "new-name");
System.out.println("preName:" + preName);
System.out.println("name:" + name);
}
and its output is:
preName:init-name
name:new-name
Why did name
change, but preName
keep the original value?
compareAndSwapObject()
does not work the way you (probably) expect. This could be so due to slightly confusing name.
It does not actually swaps the objects. It changes only name
by checking if name == preName
, and if so, it just updates its value. There's always one target field to be updated, not two.
BTW, here are good unofficial docs for Unsafe
class. You can get some useful information from it.