I was reading about AtomicInteger and how its operations are atomic and how these properties make it useful for multithreading.
I wrote the following program to test the same.
I am expecting the final size of the set should be 1000, since each thread loops 500 times and assuming each time a thread calls getNext() it should get a unique number.
But the output is always less than 1000. What am i missing here?
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());
}
}
You are missing that HashSet is not thread-safe. In addition the properties of a set would erase all duplicated numbers, so your test would fail if AtomicInteger was not thread-safe.
Try using a ConcurrentLinkedQueue instead.
Edit: Because it has been asked twice: Using a synchronized set works, but it destroys the idea behind using a lock-free algorithm like the Atomic-classes. If in your code above you replace the set with a synchronized set, then the threads will have to block each time
add
is called.This will effectively reduce your application to single-threaded, because the only work done happens synchronized. Actually it will even be slower than single-threaded, because
synchronized
takes its toll as well. So if you want to actually utilize threading, try to avoidsynchronized
at all cost.