2012-06-20 27 views
3

在像Android这样的多线程环境中,一个简单的int变量可能会被多个线程操纵,在这种情况下,仍然有理由将int用作数据成员吗?是否有任何理由不总是使用AtomicInteger作为数据成员?

作为局部变量的一个int,限于对其进行独占访问的方法的范围(并且因此开始修改它总是在同一个线程中),这在性能上是非常有意义的。

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

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

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

+0

对int的操作是不是原子的? (诚​​实的问题,这对我来说是一个新话题。) – djechlin

+0

@djechlin即使是'++'也不是原子的。 – ef2011

+1

'++'不是原子的,但读取是写入(赋值)。换句话说,你永远不会得到交错的字节。 '++'的问题是'i = i + 1'的简写,并且在读和赋值之间可能会有一个突变。 – pamphlet

回答

8

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

是的,有很好的理由总是用AtomicInteger。由于volatile构造比本地int和其他Unsafe构造被用于设置/获得底层int值,因此可能至少比一个数量级慢(可能更多)。 volatile意味着您在访问AtomicInteger时每隔越过内存障碍,这会导致相关处理器上的高速缓存内存刷新。

此外,正因为您已将所有字段设置为AtomicInteger,所以在访问多个字段时不会保护您免受竞争条件的影响。关于何时使用volatile,​​和Atomic*类别做出正确的决定是无可替代的。

例如,如果你在,你想在一个可靠的方式来访问一个线程程序类有两个字段,那么你会做这样的事情:

synchronized (someObject) { 
    someObject.count++; 
    someObject.total += someObject.count; 
} 

如果这两个部件对AtomicInteger然后你会访问volatile两次,所以跨越2个内存屏障而不是1个。另外,分配比在AtomicInteger内部的Unsafe操作更快。另外,由于两个操作的数据竞争条件(与上面的​​块相对),您可能无法获得total的正确值。

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

从使它final

除了,存在一种用于除了将其标记为volatile,或使用AtomicInteger线程安全int数据成员的机制。没有什么奇妙的方法可以在所有字段上绘制线程安全。如果有的话,线程编程将很容易。挑战是找到合适的地方放置您的​​区块。找到应该标记为volatile的正确字段。找到合适的地方使用AtomicInteger和朋友。

+0

好的。那么,如何保证'int'数据成员的类的线程安全? – ef2011

+0

你必须在课堂上正确“同步”。请参阅http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html – Gray

+0

是的,但没有锁定*整个类*效率更低? – ef2011

0

这取决于它如何使用wrt。其他数据。一个类封装了一个行为,所以如果没有其他变量,变量几乎是毫无意义的。在这种情况下,保护(*)属于一起的数据成员(或整个对象)可能会更好,而不仅仅是一个整数。如果你这样做,那么AtomicInteger是使用常见的线程安全机制不必要的性能影响

(*):互斥,信号,监控等

1

如果你有effecitvely不变int是你可以逃脱不以计算为代价确保同步。一个例子是hashCode

int hash = 0; 

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

这里最明显的权衡是相同的散列值多重计算的可能性,但如果选择是一个​​的hashCode能够具有影响差远了。

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

+0

...这不是线程安全的吗?线程1做hash == 0,线程2做hash == 0,1计算并返回,2计算并返回。不同的值来自相同的hashCode。 – djechlin

+0

除了重复的计算,你会产生什么副作用? –

+0

在线程环境中,hashCode可以在不同的调用中返回不同的值,但在非线程环境中这不是真的。 – djechlin

0

线程安全不仅是关于atomic int赋值,您需要仔细设计您的锁定模式以获得代码中的一致性。

如果您有两个Account类与公共数据成员Balance请考虑以下简单的代码。

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 
} 

真的很坦率。在现实生活中,原子分配很难解决我的现实生活中的并发问题。

相关问题