Reputation: 27435
I was asked the following question on an interview. Given the following code, if methods add
and doAction
are being invoked by multiple threads, how can we get a NullPointerException
when printing toString
?**
public class Test{
private List<Object> obj = new ArrayList<Object>();
public void add(Object o){
obj.add(o);
}
public void doAction(){
for(Object o: obj){
System.out.println(o.toString()); // maybe NPE, why?
}
}
}
Cut out all other multithread concerns.
Upvotes: 5
Views: 286
Reputation: 40256
I don't like this question because it implies knowing implementation of the ArrayList
.
If we are assuming the Object
being passed is not null then you'd have to reason with code. Here is what add looks like
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
So how can it happen? Well imagine the size
field is incremented successfully but the assignment elementData
isn't yet available to the reading thread.
In this case the iterator would size
number of elements exist and can return a null one (as it hasn't finished writing).
Essentially, it's two steps
(1) can succeed while (2) is still in flight.
Upvotes: 4
Reputation: 27190
First, let's change the name of the variable; List<Object> list=new ArrayList<>();
because "obj" is a really awful name for a variable that refers to a List
.
OK, When the program calls list.add(o);
, it may need to grow the array. That means it's got to:
null
, andIf thread A is doing that while at the same time thread B is calling iterator.next()
, thread B could end up reading a null
value from the new array even after thread A has already copied an object reference into that member of the array.
Remember: When threads access memory with no synchronization, then it is possible for a reader thread to see updates to variables/fields/array members happen in a different order from the program order in which the writing thread actually performed them.
Upvotes: 7
Reputation: 40894
One possible way:
obj
are mutable..toString
method looks at some of the mutable fields and fails if that field is null
, or maybe when the field is null
and some other condition does not hold. Quite possibly, the methods of the object mutate it so that after the method's completion, the state of the object is consistent and .toString
would not fail..toString
is called by another thread, and the call ends up with an NPE.It's hard to tell without a stacktrace.
Upvotes: 2
Reputation: 25603
Test t = new Test();
t.add(null);
t.doAction(); //NPE triggered
No nullability guarentees on the obj
list, so it might contain null values.
Regarding the problem with multithreading refers to the ConcurrentModificationException
since the "for-all" look uses an Iterator
internally. If an element is added while iterating it will cause an exception to be thrown.
Upvotes: 4