AtomicBoolean vs synchronized块

我试图通过用AtomicBoolean替换一些synchronized块来减少代码中的线程争用。

这是一个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块快一点。

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

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

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

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

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

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

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

编辑

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

在这一点上,我应该问你为什么要把注意力集中在这个特定的方面。 您是否已分析过您的应用程序并确定您确实存在锁定争用问题? 或者你只是猜测? 你有没有获得关于过早优化的邪恶的标准讲座?

看看实际的实现,我的意思是看代码比某些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 x if it is currently * holding expected. * @return true if successful */ public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x); 

这没有什么神奇之处,资源争用是一个婊子而且非常复杂。 这就是为什么使用final变量和使用不可变数据在真正的并发语言如Erlang中如此普遍。 所有这些耗费CPU时间的复杂性都是通过,或者至少转移到不那么复杂的地方。

Interesting Posts