我试图通过用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属性应该比依赖同步要快得多,所以我运行了一个little micro-benchmark。
对于10个并发线程和1000000次迭代,AtomicBoolean仅比synchronized块稍微快一点。
使用AtomicBoolean时在toggleCondition()上花费的平均时间(每个线程):0.0338
已同步的toggleCondition()上花费的平均时间(每个线程):0.0357
我知道微基准是物有所值的,但是差别不应该更大吗?
发布于 2010-10-03 09:20:05
我知道微基准是值得的,但是差别不应该更大吗?
我认为问题出在您的基准测试中。看起来每个线程只会切换一次条件。基准测试将花费大部分时间来创建和销毁线程。任何给定的线程在任何其他线程切换条件的同时切换该条件的可能性将接近于零。
当存在对条件的严重争用时,AtomicBoolean比基元锁定具有性能优势。对于非争用的情况,我希望看到很小的差异。
更改您的基准测试,以便每个线程切换该条件几百万次。这将保证大量的锁争用,我希望您会看到性能上的差异。
编辑
如果您打算测试的场景只涉及每个线程(和10个线程)一个切换,那么您的应用程序不太可能遇到争用,因此使用AtomicBoolean也不太可能有任何不同。
在这一点上,我应该问一下,为什么您要将注意力集中在这个特定的方面。您是否分析了您的应用程序并确定确实存在锁争用问题?或者你只是在猜测?你听过关于过早优化的危害的标准讲座了吗??
发布于 2010-10-03 09:24:54
看看实际的实现,我的意思是,看代码比一些微基准测试(在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变量和处理不可变数据在真正的并发语言中如此流行的原因。所有这些消耗CPU时间的复杂性都被忽略了,或者至少转移到了不那么复杂的地方。
https://stackoverflow.com/questions/3848070
复制相似问题