I have a Java class, here's its code:
public class MyClass {
private AtomicInteger currentIndex;
private List<String> list;
MyClass(List<String> list) {
this.list = list; // list is initialized only one time in this constructor and is not modified anywhere in the class
this.currentIndex = new AtomicInteger(0);
}
public String select() {
return list.get(currentIndex.getAndIncrement() % list.size());
}
}
Now my question:
Is this class really thread safe thanks to using an AtomicInteger
only or there must be an addional thread safety mechansim to ensure thread-safety (for example locks)?
The use of currentIndex.getAndIncrement()
is perfectly thread-safe. However, you need a change to your code to make it thread-safe in all circumstances.
The fields currentIndex
and list
need to be made final
to achieve full thread-safety, even on unsafe publication of the reference to your MyClass
object.
private final AtomicInteger currentIndex;
private final List<String> list;
In practice, if you always ensure that your MyClass
object itself is safely published, for example if you create it on the main thread, before any of the threads that use it are started, then you don't need the fields to be final
.
Safe publication means that the reference to the MyClass
object itself is done in a way that has a guaranteed multi-threaded ordering in the Java Memory Model.
It could be that:
- All threads that use the reference get it from a field that was initialized by the thread that started them, before their thread was started
- All threads that use the reference get it from a method that was synchronized on the same object as the code that set the reference (you have a synchronized getter and setter for the field)
- You make the field that contains the reference
volatile
- It was in a
final
field if that final field was initialized as described in section 17.5 of the JLS. - A few more cases the are not easily used to publish references