在像Android这样的多线程环境中,一个简单的int
变量可能被多个线程操纵,是否仍然有理由使用int
作为数据成员?
int
作为局部变量,仅限于具有独占访问权限的方法的范围(因此开始和完成修改它总是在同一个线程中),使得完美感觉性能 - 明智的。
但作为数据成员,即使被访问者包装,也会遇到众所周知的并发交错修改问题。
所以看起来“玩得安全”可以全面使用AtomicInteger
。但这似乎非常低效。
您能带一个线程安全int
数据成员用法的例子吗?
答案 0 :(得分:8)
有没有理由不总是使用AtomicInteger作为数据成员?
是的,不始终使用AtomicInteger
是有充分理由的。因为AtomicInteger
构造而不是本地volatile
而其他int
构造用于设置/ Unsafe
,所以int
可以至少慢一个数量级(可能更多)获取基础volatile
值。 AtomicInteger
表示您在访问AtomicInteger
时每隔时间跨越一个内存屏障,这会导致相关处理器上的缓存内存冲洗。
此外,仅仅因为您已将所有字段设为volatile
,在访问多个字段时无法保护您免受竞争条件的影响。关于何时使用synchronized
,Atomic*
和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