compareAndSet()
:
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
compareAndSwapInt()
уже является нативным:
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
Где Atomic::cmpxchg
является сгенерировал где-то в начале выполнения JVM, так как
address generate_atomic_cmpxchg() {
StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg");
address start = __ pc();
__ movl(rax, c_rarg2);
if ( os::is_MP() ) __ lock();
__ cmpxchgl(c_rarg0, Address(c_rarg1, 0));
__ ret(0);
return start;
}
cmpxchgl()
генерирует код x86 (он также имеет более длинный и устаревший путь к коду, поэтому я нескопируйте это здесь):
InstructionMark im(this);
prefix(adr, reg);
emit_byte(0x0F);
emit_byte(0xB1);
emit_operand(reg, adr);
0F
B1
- это действительно операция CMPXCHG
.Если вы проверите код, приведенный выше, if ( os::is_MP() ) __ lock();
выдает префикс LOCK
на многопроцессорных машинах (позвольте мне пропустить кавычку lock()
, он генерирует один F0
байт), то есть практически везде.
И, как сказано в CMPXCHG
документах:
Эту инструкцию можно использовать с префиксом LOCK, чтобы разрешить выполнение инструкции атомарно.Чтобы упростить интерфейс с шиной процессора, операнд-получатель получает цикл записи без учета результата сравнения.Операнд-адресат записывается обратно, если сравнение не удается;в противном случае исходный операнд записывается в место назначения.( Процессор никогда не производит заблокированное чтение, не производя также заблокированную запись. )
Таким образом, на многопроцессорной машине x86 NOP-CAS также выполняет запись, влияющую на кэш.-линия.(Акцент был добавлен мной)