St.Antario
St.Antario

Reputation: 27435

How can we get NPE, race condition

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

Answers (4)

John Vint
John Vint

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. Increment size
  2. Write elementData[size] = e

(1) can succeed while (2) is still in flight.

Upvotes: 4

Solomon Slow
Solomon Slow

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:

  1. Allocate a new, bigger array whose members all will be initialized to null, and
  2. Copy the elements from the old array to the new array.,

If 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

9000
9000

Reputation: 40894

One possible way:

  • Objects in obj are mutable.
  • The .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.
  • Another thread mutates the instance being printed. While the mutation is halfway through, and the state of the object is inconsistent, .toString is called by another thread, and the call ends up with an NPE.

It's hard to tell without a stacktrace.

Upvotes: 2

Kiskae
Kiskae

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

Related Questions