Установка AtomicBoolean снова - PullRequest
0 голосов
/ 22 ноября 2018

Я использую AtomicBoolean для обеспечения volatile видимости между потоками.Один поток обновляет значение, другой поток только читает его.

Скажем, текущее значение true.Теперь, скажем, поток записи снова устанавливает его значение на true:

final AtomicBoolean b = new AtomicBoolean(); // shared between threads

b.set(true);
// ... some time later
b.set(true);

После этого "фиктивного" set(true), есть ли снижение производительности, когда поток чтения звонки get()?Должен ли поток чтения перечитать и кэшировать значение?

Если это так, поток записи мог бы сделать:

b.compareAndSet(false, true);

Таким образом, нить чтения должна аннулировать только реальные изменения.

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

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 также выполняет запись, влияющую на кэш.-линия.(Акцент был добавлен мной)

0 голосов
/ 22 ноября 2018

И запись, и CAS "касаются" строки кэша, вызывая ее загрязнение.

Однако стоимость относительно невелика, порядка 30 - 50 нс.

Стоимость кода, который не прогревается, поскольку он еще не был выполнен 10000 раз, вероятнабыть намного большим.

...