问题描述
我在课堂上定义了一个atomicinterger变量。该类还扩展了线程类。我创建了两个线程,并在run方法中增加了原子整数的值。我在运行两个线程后排除了2的值,但是我得到的值是1。请让我知道发生了什么下面的代码中有误。
public class AtomicIntergerTest extends Thread {
AtomicInteger i = new AtomicInteger();
int j = 0;
@Override
public void run() {
i.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
// Todo Auto-generated method stub
AtomicIntergerTest th1 = new AtomicIntergertest();
AtomicIntergerTest th2 = new AtomicIntergertest();
th1.start();
th2.start();
th1.join();
th2.join();
System.out.println(th2.i.get());
}
}
解决方法
因为AtomicInteger i = new AtomicInteger();
在每个实例中创建一个AtomicInteger
。你想要
static AtomicInteger i = new AtomicInteger();
,
您说:
如果仍然有一个静态整数,我仍然会感到困惑,因为我可以在所有线程上看到相同的数据,并且可以实现线程安全性
您误会了。不,你不能。
VM基本上是这样的:
对于任何给定的字段,所有线程都具有该字段的本地副本,并且它们都有不公平的硬币。他们在读取或写入时随时翻转它。如果硬币落在正面,他们将同步该字段并对其进行读写。如果硬币落在尾巴上,则它们会读取/写入副本。代币不公平,因为它会惹上您:在测试期间和在您的机器上始终翻转头,在向该重要客户进行演示时,几次反复甩尾。
因此,如果我们在一个线程中运行此代码:
x = x + 1;
和另一个线程中的相同代码,x只能递增一次,因为第二个线程读取了它的克隆副本,因此第一个线程的增量被忽略了。或不。也许今天您尝试了100次,而每次的结果却增加了+2。然后,winamp上的下一首歌曲开始,或者月亮的相位发生变化,现在看来它只能可靠地加1。这一切都可能发生,这就是这种代码从根本上被破坏的原因。 除非您已经非常小心地简化了该字段,否则请勿与多个线程中的一个字段进行交互。
除非建立所谓的先有后继关系,否则您不能强行使用硬币。您可以通过使用volatile或sync,或通过调用执行此操作的代码来执行此操作。它变得复杂。很快。
AtomicInteger
是另一种解决方案。这是原子的。如果您的线程具有:
atomicInt.incrementAndGet();
和另一个具有相同功能的对象,在它们都运行之后,保证atomicInt已经增加了2。