我正在阅读有关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());
}
}
答案 0 :(得分:3)
您缺少HashSet不是线程安全的。此外,集合的属性将擦除所有重复的数字,因此如果AtomicInteger不是线程安全的,则测试将失败。
请尝试使用ConcurrentLinkedQueue。
编辑:因为已经被问了两次:使用同步集合可以工作,但它破坏了使用像Atomic-classes这样的无锁算法的想法。如果在上面的代码中用同步集替换了set,那么每次调用add
时线程都必须阻塞。
这将有效地将您的应用程序减少到单线程,因为完成的唯一工作是同步的。实际上它甚至比单线程慢,因为synchronized
也会收费。因此,如果您想要实际使用线程,请尽量避免使用synchronized
。
答案 1 :(得分:1)
HashSet不是线程安全的,因此您遇到问题。如果您非常需要使用HashSet,您可以使用Vector或任何线程安全的集合类或顺序运行两个线程。
t1.start();
t1.join();
t2.start();
t2.join();
答案 2 :(得分:0)
正如在几个答案中所提到的,由于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的最终值,所有其他值都会被覆盖并丢失。
类似的事情可能就是你的测试中发生的事情。
答案 3 :(得分:-1)
尝试使用同步集合以这种方式执行此操作。
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();
}
}