AtomicBoolean против синхронизированного блока

Я пытался сократить конфликт потоков в моем коде, заменив некоторыеsynchronized блоки сAtomicBoolean.

Вот пример сsynchronized:

public void toggleCondition() {
    synchronized (this.mutex) {
        if (this.toggled) {
            return;
        }

        this.toggled = true;
        // do other stuff
    }
}

И альтернатива сAtomicBoolean:

public void toggleCondition() {
    if (!this.condition.getAndSet(true)) {
        // do other stuff
    }
}

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

Для 10 одновременных потоков и 1000000 итераций,AtomicBoolean приходит только немного быстрее, чемsynchronized блок.

Среднее время (на поток), затраченное на toggleCondition () с AtomicBoolean: 0,0338

Среднее время (на поток), затраченное на toggleCondition () с синхронизированным: 0,0357

Я знаю, что микропроцессоры стоят того, чего они стоят, но не должна ли разница быть выше?

 biasedbit03 окт. 2010 г., 04:20
После другого микро-теста (да, я знаю, что микро-тесты несколько ошибочны ...) я пришел к выводу, что проблема заключается в использовании CountDownLatch вместо wait () / notify (). Кроме того, этот подход AtomicBoolean не был полностью безопасным, поэтому я отбросил его.
 matt b03 окт. 2010 г., 03:57
Возможно, у вас было очень мало споров в исходном коде для начала. Что привело вас к мысли, что это нужно оптимизировать?
 biasedbit03 окт. 2010 г., 04:16
Выполнение тестов производительности на lib я написал. У меня была реализация по умолчанию, использующая синхронизированные блоки и ручную синхронизацию wait () / notify (). Затем я попробовал другую версию, используя только java.util.concurrent, и получил худшие результаты. Вот классы:github.com/brunodecarvalho/hotpotato/blob/master/src/main/java/... а такжеgithub.com/brunodecarvalho/hotpotato/blob/master/src/main/java/...

Ответы на вопрос(2)

Решение Вопроса

что микропроцессоры стоят того, чего они стоят, но не должна ли разница быть выше?

Я думаю, что проблема в вашем тесте. Похоже, что каждый поток будет переключать условие только один раз. Тест будет тратить большую часть своего времени на создание и уничтожение потоков. Вероятность того, что любой данный поток будет переключать условие одновременно с любым другим потоком, будет близка к нулю.

AtomicBoolean имеет преимущество в производительности по сравнению с примитивной блокировкой, когда существует серьезная конкуренция за условие. Для неудовлетворенного состояния я бы ожидал увидеть небольшую разницу.

Измените свой тест, чтобы каждый поток переключал условие несколько миллионов раз. Это гарантирует большую конкуренцию за блокировку, и я ожидаю, что вы увидите разницу в производительности.

РЕДАКТИРОВАТЬ

Если в сценарии, который вы намеревались протестировать, использовался только один переключатель на поток (и 10 потоков), маловероятно, что ваше приложение столкнется с конкуренцией, и, следовательно, маловероятно, что использование AtomicBoolean будет иметь какое-либо значение.

На этом этапе я должен спросить, почему вы концентрируете свое внимание на этом конкретном аспекте. Профилировали ли вы ваше заявление и определили, чтодействительно у вас проблема с конфликтом блокировок? Или ты просто угадаешь? Вам уже дали стандартную лекцию о пороках преждевременной оптимизации?

 biasedbit03 окт. 2010 г., 06:15
В некотором смысле это противоречит сценарию, который я намеревался проверить (может произойти только одно переключение), но я попробую ...
 biasedbit03 окт. 2010 г., 20:03
Что касается вашего редактирования, да, у меня есть. У меня нет проблем с производительностью, мне просто было любопытно; В последний раз я слышал, что это не плохо.
 Stephen C03 окт. 2010 г., 05:41
Как я уже сказал ... изменить каждую веткуrun метод для вызова toggleCondition () большое количество раз.
 Stephen C27 мар. 2013 г., 00:06
@brunodecarvalho - любопытство не плохая вещь. Тем не менее, вы должны сосредоточить свое любопытство на том, что может иметь значение. В противном случае вы просто тратите свое время ... как охота на единорогов.
 Stephen C03 окт. 2010 г., 04:20
Я знаю это. Проблема в том, что способ написания эталонного теста означает, что споров почти нет. Измените код так, чтобы произошла значительная конкуренция, и я ожидаю, что вы увидите разницу в производительности.
 biasedbit03 окт. 2010 г., 04:11
Я только синхронизирую время, которое требуется для вызова toggleCondition ().
 biasedbit03 окт. 2010 г., 04:39
Проверьте мои ответы на комментарий от Matt B на вопрос. В реальном случае я читал худшие значения для версии, используя AtomicBoolean; но кажется, что это на самом деле CountDownLatch вызывает замедление. Тем не менее, как вы предлагаете мне увеличить раздор там?

я имею в виду, что смотреть на код гораздо лучше, чем какой-то микробенчмарк (который менее чем бесполезен в Java или любой другой среде выполнения GC), я не удивлен, что он «значительно быстрее». Это в основном делает неявный синхронизированный раздел.

/**
 * Atomically sets to the given value and returns the previous value.
 *
 * @param newValue the new value
 * @return the previous value
 */
public final boolean getAndSet(boolean newValue) {
    for (;;) {
        boolean current = get();
        if (compareAndSet(current, newValue))
            return current;
    }
}

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 *
 * @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 final boolean compareAndSet(boolean expect, boolean update) {
    int e = expect ? 1 : 0;
    int u = update ? 1 : 0;
    return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}

И тогда это изcom.sun.Unsafe.java

/**
 * Atomically update Java variable to <tt>x</tt> if it is currently
 * holding <tt>expected</tt>.
 * @return <tt>true</tt> if successful
 */
public final native boolean compareAndSwapInt(Object o, long offset,
                                              int expected,
                                              int x);

в этом нет ничего волшебного, разногласия в ресурсах - сука и очень сложны. Вот почему с помощьюfinal Переменные и работа с неизменяемыми данными так распространены в реальных параллельных языках, таких как Erlang. Вся эта сложность, которая съедает процессорное время, пропущена или, по крайней мере, сдвинута куда-то менее сложным

Ваш ответ на вопрос