Реализация AtomicByteArray в Java с использованием AtomicIntegerArray

#java #concurrency #atomic-values

#java #параллелизм #атомарные значения

Вопрос:

Мне нужен AtomicByteArray для критически важного для памяти приложения, созданного по образцу AtomicIntegerArray Java. Моя реализация преобразует четыре байта в целое число и использует AtomicIntegerArray.

get() Реализация тривиальна, а set() реализации and довольно просты. Чем compareAndSwap() сложнее. Моя реализация приведена ниже (она отлично работает в однопоточном режиме).

Я пытаюсь определить состояние гонки. Один из возможных сценариев заключается в том, что значения изменяются и меняются местами между вызовами get() и compareAndSet() , но это кажется безвредным.

Я пропустил что-нибудь, что может пойти не так?

 /**
 * Atomically sets the element at position {@code i} to the given
 * updated value if the current value {@code ==} the expected value.
 *
 * @param i the index
 * @param expect the expected value
 * @param update the new value
 * @return true if successful. False return indicates that
 * the actual value was not equal to the expected value.
 */
public boolean compareAndSet(final int i, final byte expected, final byte val) {
    int idx = i >>> 2;
    int shift = (i amp; 3) << 3;

    while (true) {
        final int num = this.array.get(idx);
        // Check that the read byte is what we expected
        if ((byte)(num >> shift) != expected) {
            return false;
        }
        // If we complete successfully, all is good
        final int num2 = (num amp; ~(0xff << shift)) | ((val amp; 0xff) << shift);
        if ((num == num2) || this.array.compareAndSet(idx, num, num2)) {
            return true;
        }
    }
}
  

Обновление: я внедрил базовую версию AtomicByteArray, которая объединяет улучшения в приведенном ниже ответе.

Комментарии:

1. Я бы i / 4 -> i >>> 2 и 8 * (i % 4) -> (i amp; 3) << 3

2. Относится ли это к проверке кода ?

3. @PeterLawrey Спасибо за предложение. Исправлено.

4. @JimGarrison Я не знаю, но я рад сделать репост там. Это скорее вопрос параллелизма, чем вопрос кода Java.

5. Именно поэтому я попросил вместо голосования закрыть тему. Мне любопытно, к чему относится этот вопрос.

Ответ №1:

Вы могли бы рассмотреть возможность использования маски. Это может быть быстрее / чище.

 int idx = i >>> 2;
int shift = (i amp; 3) << 3;
int mask = 0xFF << shift;
int expected2 = (expected amp; 0xff) << shift;
int val2 = (val amp; 0xff) << shift;

while (true) {
    final int num = this.array.get(idx);
    // Check that the read byte is what we expected
    if ((num amp; mask) != expected2) return false;
    // If we complete successfully, all is good
    final int num2 = (num amp; ~mask) | val2;
    if ((num == num2) || this.array.compareAndSet(idx, num, num2)) {
        return true;
    }
}
  

Вы хотите выполнить минимум работы внутри цикла, чтобы в случае возникновения разногласий вы могли повторить попытку как можно быстрее.

Я не уверен num == num2 , что это помогает больше, чем вредит. Я предлагаю вам попробовать это без сравнения.

Комментарии:

1. Спасибо! Я предложил несколько исправлений, связанных с подписью и байтом в int для продвижения. Чтобы быть точным, когда вы выполняете побитовые операции над -1 (0xff), он сначала повышается до int -1 (0xfffffff).

2. Я реализовал базовую версию, которая объединяет ваши предложения в github.com/shilad/wikibrain/blob/master/wikibrain-utils/src /…