有没有理由不总是使用AtomicInteger作为数据成员?

时间:2012-06-20 18:01:52

标签: java multithreading performance atomicity

在像Android这样的多线程环境中,一个简单的int变量可能被多个线程操纵,是否仍然有理由使用int作为数据成员?

int作为局部变量,仅限于具有独占访问权限的方法的范围(因此开始和完成修改它总是在同一个线程中),使得完美感觉性能 - 明智的。

但作为数据成员,即使被访问者包装,也会遇到众所周知的并发交错修改问题。

所以看起来“玩得安全”可以全面使用AtomicInteger。但这似乎非常低效。

您能带一个线程安全int数据成员用法的例子吗?

5 个答案:

答案 0 :(得分:8)

  

有没有理由不总是使用AtomicInteger作为数据成员?

是的,始终使用AtomicInteger是有充分理由的。因为AtomicInteger构造而不是本地volatile而其他int构造用于设置/ Unsafe,所以int可以至少慢一个数量级(可能更多)获取基础volatile值。 AtomicInteger表示您在访问AtomicInteger时每隔时间跨越一个内存屏障,这会导致相关处理器上的缓存内存冲洗。

此外,仅仅因为您已将所有字段设为volatile,在访问多个字段时无法保护您免受竞争条件的影响。关于何时使用synchronizedAtomic*synchronized (someObject) { someObject.count++; someObject.total += someObject.count; } 类做出正确的决定是无可替代的。

例如,如果您想在一个线程程序中以可靠的方式访问类中的两个字段,那么您可以执行以下操作:

AtomicInteger

如果这两个成员都volatile,那么您将访问Unsafe两次,从而跨越2个内存障碍,而不仅仅是1.此外,分配比AtomicInteger操作更快在synchronized内。此外,由于两个操作的数据竞争条件(与上面的total块相对),您可能无法获得final的正确值。

  

你能带一个线程安全的int数据成员用法的例子吗?

除了将int标记为volatile或使用AtomicInteger之外,没有机制可用于线程安全synchronized数据成员。没有神奇的方法可以在所有字段上绘制线程安全性。如果有,那么线程编程将很容易。挑战在于找到放置volatile块的正确位置。要查找应使用AtomicInteger标记的正确字段。找到合适的地方{{1}}和朋友。

答案 1 :(得分:1)

如果你有一个有效的int,那么你可以通过计算来避免同步。一个例子是hashCode

int hash = 0;

public int hashCode(){
   if(hash == 0){
     hash = calculateHashCode(); //needs to always be the same for each Object
   }
   return hash;
}

这里显而易见的权衡是对同一个哈希值进行多次计算的可能性,但如果备选方案是synchronized hashCode可能会产生更糟糕的影响。

这在技术上是线程安全的,虽然多余。

答案 2 :(得分:0)

这取决于它是如何使用的。其他数据。一个类封装了一个行为,因此如果没有其他变量,变量通常几乎没有意义。在这种情况下,保护(*)属于一起的数据成员(或整个对象)可能更好,而不是仅保留一个整数。如果您这样做,那么AtomicInteger是不必要的性能损失

(*)使用常见的线程安全机制:互斥,信号量,监视器等。

答案 3 :(得分:0)

线程安全不仅与原子int赋值有关,还需要仔细设计锁定模式以使代码保持一致。

如果您有两个带有公共数据名称Account的{​​{1}}个类,请考虑以下简单代码。

Balance

坦白说实话。在现实生活中,拥有原子分配很少能够解决我现实生活中的并发问题。

答案 4 :(得分:0)

我想Google看到了OP,并更新了有关该主题的文档:

“ AtomicInteger用于原子增量计数器之类的应用程序,不能用作Integer的替代。”

https://developer.android.com/reference/java/util/concurrent/atomic/AtomicInteger