9

一部の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
    }
}

の CAS プロパティを利用AtomicBooleanすると、同期に依存するよりもはるかに高速になるはずなので、ちょっとしたマイクロ ベンチマークを実行しました。

10 個の同時スレッドと 1000000 回の反復の場合、ブロックAtomicBooleanよりわずかに高速になります。synchronized

AtomicBoolean を使用した toggleCondition() に費やされた平均時間 (スレッドあたり): 0.0338

同期された状態で toggleCondition() に費やされた平均時間 (スレッドごと): 0.0357

マイクロベンチマークが価値があることは知っていますが、その差はもっと大きくなるべきではありませんか?

4

2 に答える 2

6

マイクロベンチマークが価値があることは知っていますが、その差はもっと大きくなるべきではありませんか?

問題はベンチマークにあると思います。各スレッドが条件を一度だけ切り替えようとしているようです。ベンチマークは、ほとんどの時間をスレッドの作成と破棄に費やします。特定のスレッドが条件を切り替えると同時に、他のスレッドが条件を切り替える可能性はゼロに近くなります。

AtomicBoolean は、条件に重大な競合がある場合、プリミティブ ロックよりもパフォーマンスが優れています。競合していない状態の場合、ほとんど違いが見られないと思います。

各スレッドが条件を数百万回トグルするようにベンチマークを変更します。これにより、多くのロック競合が保証され、パフォーマンスの違いが見られると思います.

編集

テストしようとしていたシナリオがスレッドごとに 1 つのトグル (および 10 スレッド) のみを含む場合、アプリケーションで競合が発生する可能性は低いため、AtomicBoolean を使用しても違いが生じる可能性はほとんどありません。

この時点で、なぜこの特定の側面に注意を向けているのかを尋ねなければなりません。アプリケーションのプロファイリングを行い、本当にロック競合の問題があると判断しましたか? それともただの推測ですか?時期尚早の最適化の弊害についての標準的な講義はもう受けましたか??

于 2010-10-03T01:20:04.673 に答える
3

実際の実装を見ると、コードを見る方がいくつかのマイクロベンチマーク(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のような実際の並行言語では、変数の使用と不変データの操作が非常に普及しています。CPU時間を消費するこの複雑さはすべて過ぎ去るか、少なくともそれほど複雑ではない場所にシフトされます。

于 2010-10-03T01:24:53.900 に答える