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

Java 是否有理由不总是使用AtomicInteger作为数据成员?,java,multithreading,performance,atomicity,Java,Multithreading,Performance,Atomicity,在Android这样的多线程环境中,一个简单的int变量可能会被多个线程操作,在这种情况下,是否仍有理由将int用作数据成员 int作为一个局部变量,仅限于对其具有独占访问权限的方法的范围(因此修改它的开始和结束总是在同一个线程中),在性能方面非常有意义 但作为数据成员,即使由访问器包装,它也可能遇到众所周知的并发交错修改问题 因此,为了“安全起见”,我们可以全面使用AtomicInteger。但这似乎效率极低 你能举一个线程安全int数据成员使用的例子吗 是否有理由不总是使用AtomicInt

在Android这样的多线程环境中,一个简单的
int
变量可能会被多个线程操作,在这种情况下,是否仍有理由将
int
用作数据成员

int
作为一个局部变量,仅限于对其具有独占访问权限的方法的范围(因此修改它的开始和结束总是在同一个线程中),在性能方面非常有意义

但作为数据成员,即使由访问器包装,它也可能遇到众所周知的并发交错修改问题

因此,为了“安全起见”,我们可以全面使用
AtomicInteger
。但这似乎效率极低

你能举一个线程安全
int
数据成员使用的例子吗

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

是的,有充分的理由不总是使用
AtomicInteger
<由于
volatile
构造比本地
int
和其他用于设置/获取基础
int
值的
不安全
构造慢至少一个数量级(可能更多)
volatile
表示每次访问原子整数时都会跨越内存障碍,这会导致处理器上的缓存刷新

此外,仅仅因为您将所有字段设置为
AtomicInteger
,并不能在访问多个字段时保护您免受竞争条件的影响。对于何时使用
volatile
synchronized
、以及
原子*
类,做出正确的决策是无可替代的

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

synchronized (someObject) {
   someObject.count++;
   someObject.total += someObject.count;
}
如果这两个成员都使用
AtomicInteger
,那么您将访问
volatile
两次,因此将跨越2个内存屏障,而不是仅1个。此外,赋值比
AtomicInteger
中的
不安全操作要快。此外,由于这两个操作的数据竞争条件(与上面的
synchronized
块相反),您可能无法获得
total
的正确值

你能举一个线程安全int数据成员使用的例子吗


除了使其成为最终的
,没有线程安全的
int
数据成员的机制,除了将其标记为volatile或使用原子整数。没有神奇的方法可以在你所有的场地上画出安全的线。如果当时有线程编程将很容易。挑战在于找到合适的位置来放置
同步的
块。查找应标记为
volatile
的正确字段。找到使用原子整数和朋友的合适位置。

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


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

如果您有有效的不可变
int
s,您可以避免以计算为代价不确保同步。例如
hashCode

int hash = 0;

public int hashCode(){
   if(hash == 0){
     hash = calculateHashCode(); //needs to always be the same for each Object
   }
   return hash;
}
这里的一个明显的折衷是可能对同一个哈希值进行多次计算,但如果替代方法是
同步的
哈希代码,则可能产生更糟糕的影响


这在技术上是线程安全的,尽管是冗余的。

线程安全不仅仅是原子int赋值,您需要仔细设计锁定模式,以确保代码的一致性

如果你有两个“代码>帐号类,带有公共数据文件<代码>余额考虑下面的简单代码。

Account a;
...
int withdrawal = 100;
if(a.Balance >= withdrawal)
{
    // No atomic operations in the world can save you from another thread
    // withdrawing some balance here
    a.Balance -= withdrawal
}
else
{
   // Handle error
}

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

我想谷歌看到了这篇文章,并更新了他们关于这个主题的文档,使之更加清晰:

原子整数用于诸如原子递增计数器之类的应用程序,不能用作整数的替换


int上的哪些操作不是原子操作?(老实说,这对我来说是一个新话题。)@djechlin即使是
++
也不是原子。
++
不是原子,但读就是写(赋值)是原子。换句话说,您永远不会得到交错字节。
++
的问题是它是
i=i+1
的简写,读取和赋值之间可能存在变异。OK。那么,如何保证具有
int
数据成员的类的线程安全呢?必须在类周围正确地
同步
。看到了吗?是的,但是锁定整个类的效率不是更低吗?不是,因为即使访问多个字段,也只会跨越一个内存障碍。此外,它还允许您在需要时获得同步性能的影响。当您知道您正在更新对象或需要确保具有一致的同步值时。Volatile不是与AtomicInteger相关联的唯一性能影响。最终读取和写入不同且较慢的处理器指令…这不是线程安全的吗?线程1执行哈希==0,线程2执行哈希==0,1计算并返回,2计算并返回。同一个hashCode会产生不同的值。除了重复的计算之外,还会产生什么副作用?hashCode可以在不同的inv上返回不同的值