Single threaded version:
private final List<Element> list = new ArrayList<Element>();
public Element getElementAt(int index) {
if (index >= list.size()) {
for (int i = list.size(); i <= index; i++) {
list.add(createElement(i));
}
}
return list.get(index);
}
Now I am trying to make a thread-safe version with double checked locking:
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
...
private volatile List<Element> list = ImmutableList.of();
public Element getElementAt(int index) {
if (index >= list.size()) {
synchronized (this) {
if (index >= list.size()) {
Builder<Element> newListBuilder = ImmutableList.<Element> builder();
newListBuilder.addAll(list);
for (int i = list.size(); i <= index; i++) {
newListBuilder.add(createElement(i));
}
list = newListBuilder.build();
}
}
}
return list.get(index);
}
Is this correct?
What you are doing is more like a map/dictionary lookup. If you consider that your list is really behaving like a
Map<Integer, Element>
then you can use a concurrent map's putIfAbsent method to handle this without blocking:This assumes that there's no particular ordering requirements on the elements returned from
createElement
- though if that's the case, you'd need much tighter constraints on how they're created anyway.Chances are though that in reality you could simply stick a synchronized block around this method (and any other than accesses the list). It's easy to understand, and it's relatively fast. If getting elements isn't the performance hotspot of your code, I wouldn't want to do something "clever" just to shave off a couple of nanoseconds.