AtomicBoolean vs synchronized块

时间:2010-10-03 00:33:39

标签: java synchronized-block 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属性应该比依赖同步更快,所以我运行了little micro-benchmark

对于10个并发线程和1000000次迭代,AtomicBoolean仅比synchronized块稍快一些。

使用AtomicBoolean在toggleCondition()上花费的平均时间(每个线程):0.0338

使用synchronized:0.0357

在toggleCondition()上花费的平均时间(每个主题)

我知道微基准值得他们值得,但差异不应该更高吗?

2 个答案:

答案 0 :(得分:6)

  

我知道微基准值得他们值得,但差异不应该更高吗?

我认为问题出在您的基准测试中。看起来每个线程只会将条件切换一次。基准测试将花费大部分时间来创建和销毁线程。任何给定线程在任何其他线程切换的同时切换条件的可能性将接近于零。

当存在对条件的重大争用时,AtomicBoolean具有优于原始锁定的性能优势。对于无条件的情况,我希望看到的差别不大。

更改基准,以便每个线程将条件切换几百万次。这将保证很多锁争用,我希望你会看到性能差异。

修改

如果您打算测试的场景只涉及每个线程(和10个线程)的一个切换,那么您的应用程序不太可能会遇到争用,因此使用AtomicBoolean不太可能产生任何差异。

此时,我应该问你为什么要把注意力集中在这个方面。您是否已分析过您的应用程序并确定确实您有锁定争用问题?或者你只是猜测?您是否接受过关于过早优化的弊端的标准讲座?

答案 1 :(得分:3)

看看实际的实现,我的意思是看代码比某些microbenchmark更好(在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时间的复杂性都是通过,或者至少转移到不那么复杂的地方。