How can we get NPE race condition

I was asked the following question in an interview. Given the following code, if the add and doAction called 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 all other multithreaded problems.

+5
source share
4 answers

First change the variable name; List<Object> list=new ArrayList<>(); because "obj" is a really terrible name for a variable that references List .

OK. When a program calls list.add(o); You may need to increase the array. This means that he has reached:

  • Select a new, larger array whose members will be initialized to null and
  • Copy elements from the old array to the new array.,

If thread A does this, while thread B calls iterator.next() , thread B can finish reading null from the new array even after thread A has already copied the object reference to this member of the array.

Remember: when threads access memory without synchronization, then the reader thread can see updates for variables / fields / array members in a different order from the order of the program in which the write stream was actually executed.

+6
source
 Test t = new Test(); t.add(null); t.doAction(); //NPE triggered 

There are no null guarentees in the obj list, so it can contain null values.

Regarding the issue of multi-threaded access, refers to ConcurrentModificationException , since the look is used for Iterator internally. If an item is added during repetition, it will throw an exception.

+4
source

I do not like this question because it implies knowledge of the implementation of ArrayList .

If we assume that the passed Object not null, you will have to reason with the code. Here is the inscription

 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } 

So how can this happen? Imagine that the size field is successfully growing, but the assignment of elementData is not yet available to the read stream.

In this case, the iterator will size contain the number of elements and can return zero (as it has not finished writing).

These are essentially two steps.

  • Increment size
  • Write elementData [size] = e

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

+4
source

One possible way:

  • Objects in obj mutable.
  • The .toString method checks some of the mutable fields and fails if this field is null , or maybe when the field is null and some other condition is not met. It is possible that the methods of the object mutate it so that after the method finishes, the state of the object is consistent and .toString will not work.
  • Another thread mutates the instance that is being printed. Although the mutation is halfway, and the state of the object is incompatible, the .toString is called by another thread, and the call ends with NPE.

It's hard to say without stacktrace.

+2
source

Source: https://habr.com/ru/post/1266325/


All Articles