[英]AtomicInteger incrementation not behaving as expected
我正在阅读有关AtomicInteger及其操作是如何原子的以及这些属性如何使其对多线程有用的内容。
我编写了以下程序来测试相同的程序。
我期望集合的最终大小应该是1000,因为每个线程循环500次并假设每次线程调用getNext()它应该得到一个唯一的数字。
但输出总是小于1000.我在这里缺少什么?
public class Sequencer {
private final AtomicInteger i = new AtomicInteger(0);
public int getNext(){
return i.incrementAndGet();
}
public static void main(String[] args) {
final Sequencer seq = new Sequencer();
final Set<Integer> set = new HashSet<Integer>();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i=0; i<500; i++)
set.add(seq.getNext());
}
},"T1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i=0; i<500; i++)
set.add(seq.getNext());
}
},"T2");
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(set.size());
}
}
您缺少HashSet不是线程安全的。 此外,集合的属性将擦除所有重复的数字,因此如果AtomicInteger不是线程安全的,则测试将失败。
请尝试使用ConcurrentLinkedQueue 。
编辑:因为它被问过两次:使用同步集合可以工作,但它破坏了使用像Atomic-classes这样的无锁算法的想法。 如果在上面的代码中用同步集替换了set,那么每次调用add
线程都必须阻塞。
这将有效地将您的应用程序减少到单线程,因为完成的唯一工作是同步的。 实际上它甚至会比单线程慢,因为synchronized
也会造成损失。 因此,如果您想要实际使用线程,请尽量避免synchronized
。
HashSet不是线程安全的,因此您遇到问题。如果您非常需要使用HashSet,您可以使用Vector或任何线程安全的集合类或顺序运行两个线程。
t1.start();
t1.join();
t2.start();
t2.join();
正如在几个答案中提到的,由于HashSet不是线程安全的,它失败了。
首先,为了您的测试,我们验证AtomicInteger确实是线程安全的,然后继续查看您的测试失败的原因。 稍微修改你的测试。 使用两个哈希集,每个线程一个。 最后,在连接之后,将第二组合并到第一组,通过迭代第二组并将其添加到第一组,这将消除重复(set属性)。 然后对第一组进行计数。 伯爵将是你所期望的。 这证明它是HashSet不是线程安全的而不是AtomicInteger。
那么让我们来看看哪个方面不是线程安全的。 你只做add()s,所以很明显add()是非线程安全的操作导致数字丢失。 让我们看一个示例伪代码非线程安全的HashMap add(),它会丢失数字(这显然不是它实现的方式,只是试图说明它可能是非线程安全的一种方式):
class HashMap {
int valueToAdd;
public add(int valueToAdd) {
this.valueToAdd = valueToAdd;
addToBackingStore(this.valueToAdd);
}
}
如果多个线程调用add()并且在更改this.valueToAdd后它们都到达addToBackingStore(),则只添加valueToAdd的最终值,所有其他值都会被覆盖并丢失。
类似的东西可能是你的测试中发生的事情。
尝试使用Collections synchronized以这种方式执行此操作。
public class Sequencer {
private final AtomicInteger i = new AtomicInteger(0);
public static void main(String[] args) {
final Sequencer seq = new Sequencer();
final Set<Integer> notSafe = new HashSet<Integer>();
final Set<Integer> set = Collections.synchronizedSet(notSafe);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++)
set.add(seq.getNext());
}
}, "T1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500; i++)
set.add(seq.getNext());
}
}, "T2");
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(set.size());
}
public int getNext() {
return i.incrementAndGet();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.