1

Why is Arraylist's size not right when multiple threads add elements into it?

threadCount = 100;
List<Object> list = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
    Thread thread = new Thread(new MyThread(list, countDownLatch));
    thread.start();
}

class MyThread implements Runnable {
    // ......

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            list.add(new Object());
        }
    }
}

When this program is done, the size of the list should be 10000. Actually, the size may be 9950, 9965 or some other numbers. Why?

I know that why this program may raise IndexOutofBoundsException and why there are some nulls in it, but I just do not understand why the size is wrong.

Tom
  • 16,842
  • 17
  • 45
  • 54
fei_hsueh
  • 161
  • 1
  • 11
  • 4
    ArrayList is not thread safe. – Eran Oct 26 '16 at 09:02
  • 4
    From the documentation: "Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally." You're not doing that. – Jon Skeet Oct 26 '16 at 09:03
  • 1
    @fei_hsueh The docs basically say that the behaviour is unspecified - so it may or may not work... – assylias Oct 26 '16 at 09:05
  • Some more discussion, examples: http://stackoverflow.com/questions/38527429/what-are-the-possible-problems-caused-by-adding-elements-to-unsynchronized-array?rq=1 – Thilo Oct 26 '16 at 09:08
  • 1
    @fei_hsueh the question asks why the size *is* wrong, not why the size is not wrong. – Andy Turner Oct 26 '16 at 09:12
  • 1
    *"I know it."* That's a lie. If you would really know it, then this question wouldn't be here. And just google "race condition". Possible dupes: http://stackoverflow.com/questions/3836680/data-race-in-java-arraylist-class and http://stackoverflow.com/questions/34510/what-is-a-race-condition – Tom Oct 26 '16 at 09:18
  • 1
    Define 'when this program is done'. There is nothing in this code that determines when it is done, or that prints the `ArrayList` size when it has done so. – user207421 Oct 26 '16 at 09:36

5 Answers5

3

I just do not understand why the size is wrong?

As ArrayList is not thread-safe so two threads may add at same index. So one thread overwrites.

Azodious
  • 13,752
  • 1
  • 36
  • 71
  • Two threads add at same index , but the size still +1 twice. Finally, the size should be right. This just adds some nulls into list. – fei_hsueh Oct 26 '16 at 09:08
  • Check the javadoc for add method. it clearly says that element will be added at last. Now two threads find out that 'n' is last index. and both add the object at same index. and size increases by 1 only. – Azodious Oct 26 '16 at 09:11
  • 2
    "but the size still +1 twice" just not atomically. The fetching of size and storing of the new value of size by the two threads can be interleaved. For example, two threads can read `size == 0` at the same time; both then attempt to store `size = 1`. – Andy Turner Oct 26 '16 at 09:15
  • @Andy Turner: and the thread checking the result could have read the `size` field even before the other threads set it to `1`, in which case it would consider it to be `0`… – Holger Oct 27 '16 at 17:28
3

While you are using arraylist.clear() at any position ,in main ui thread ,after that if array list operation are done on background service & finally if you have array list in main ui thread after performing background operation, array list becomes zero size of list.

        //don't use it in main ui thread 
        activity?.runOnUiThread {
               // it comes myarraylist size is 0 at end
                myarraylist.clear()
            }
    ....
    
     //some background opertion add to arraylist
....
    activity?.runOnUiThread {
         //if you set adapter it may get 0 size
               
            }
aravinth C
  • 95
  • 5
1

arraylist is not thread-safe,so if any multi-thread operation on one arraylist object, the result is undefined,which means anything could happen due to jvm, including but not limited to wrong size, exception, memory leak, as expected

dexter
  • 124
  • 6
0

It Simple, You are preforming an unsafe operation on an object... So you can't predict the stability... If you run enough times there might be one instance when the list.size() will return 10000. Why Its not returning always.??. Answer is , because its not thread safe. Now consider the code of add in arraylist

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

Here suppose when thread 10 is inside "ensureCapacity" and it goes for sleep then other thread (say thread 11) will come and they( thread 10 and 11) will end up inserting 2 elements at one place. This is happening here

RAHUL ROY
  • 126
  • 2
  • 13
0

you should refer to the source code in ArrayList.

@Override public boolean add(E object) {
        Object[] a = array;
        int s = size;
        if (s == a.length) {
            Object[] newArray = new Object[s +
                    (s < (MIN_CAPACITY_INCREMENT / 2) ?
                     MIN_CAPACITY_INCREMENT : s >> 1)];
            System.arraycopy(a, 0, newArray, 0, s);
            array = a = newArray;
        }
        a[s] = object;
        size = s + 1;
        modCount++;
        return true;
    }

ArrayList uses System.arraycopy(a, 0, newArray, 0, s) to insert element. If multi threads are running, the field array that contains the elements is not the original one, and the size is less than expected.

McJudy
  • 1
  • 1