Multi Threading with ArrayList() got ArrayIndexOutOfBoundsException [duplicate] - java

This question already has answers here:
What are the possible problems caused by adding elements to unsynchronized ArrayList's object by multiple threads simultaneously?
(4 answers)
Java MultiThreading behaviour explanation
(4 answers)
Why is arraylist's size not right when multiple threads add elements into it?
(5 answers)
Data race in Java ArrayList class
(3 answers)
Closed 4 years ago.
I've made the following code to run multiple thread through a non thread safe object (here an ArrayList) :
import java.time.LocalDateTime;
import java.util.List;
import java.util.ArrayList;
public class A implements Runnable {
String name;
static List<Integer> list = new ArrayList();
private static Object lock = new Object();
A(String name) {
this.name = name;
}
#Override
public void run() {
for(int i = 1; i <= 1000; i++) {
list.add(i);
}
System.out.println(list.size());
}
}
I was expecting this code just to produce wrong answers since ArrayList is not thread-safe. But instead I get this error :
Exception in thread "Thread-1" 1003
2401
2799
3799
java.lang.ArrayIndexOutOfBoundsException: 109
at java.util.ArrayList.add(Unknown Source)
at threads.A.run(A.java:16)5123
at java.lang.Thread.run(Unknown Source)
Exception in thread "Thread-5" java.lang.ArrayIndexOutOfBoundsException: 4164
at java.util.ArrayList.add(Unknown Source)
at threads.A.run(A.java:16)
at java.lang.Thread.run(Unknown Source)
6123
Can anyone explain to me what is leading to this specific error?

Well, you are using a non thread-safe collection in a multi threaded environment without any synchronization.
Let's examine the add method, where you get the exception:
/**
* Appends the specified element to the end of this list.
*
* #param e element to be appended to this list
* #return <tt>true</tt> (as specified by {#link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
When multiple threads call this method at the same time, it is quite possible that ensureCapacityInternal(size + 1) verifies there is enough space for 1 new element, but then multiple threads try to add an element at the same time, so elementData[size++] throws ArrayIndexOutOfBoundsException for some of them.

ArrayList is not a thread-safe class.
Underlying storage for elements is Object[] which is an array. Any array requires the allocation space in memory which is predefined in the compile time. However, when an ArrayList "wants" to add the new element (beyond the underlying array bound), several things have to be done (without your knowledge). Underlying array gets a new (increased) length. Every element of the old array is copied to the new array, and then the new element is added. So, you can expect the ArrayIndexOutOfBoundsException exception when an ArrayList is used in multi-thread environment.
You are adding elements too fast so ArrayList#add() -> grow() -> newCapacity() can't calculate the correct capacity to allocate the memory for all of the elements coming in.
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
At some point of time, the condition s == elementData.length inside ArrayList#add says that there is a space for a new element A. Immediately after that other threads put their elements into the list. Now there is no space for A and elementData[s] = e; throws an exception.

Related

Using synchronizedList with for loop and adding items inside it

I'm using
Collections.synchronizedList(new ArrayList<T>())
part of the code is:
list = Collections.synchronizedList(new ArrayList<T>());
public void add(T arg) {
int i;
synchronized (list) {
for (i = 0; i < list.size(); i++) {
T arg2 = list.get(i);
if (arg2.compareTo(arg) < 0) {
list.add(i, arg);
break;
}
}
Is it right that for loop is actually using iterator and therefore I must wrap the for with synchronized?
Is it thread-safe to use synchronized and make addition inside it like I did here?
I'm sorry if these questions are very basic, I'm new to the subject and didn't find answers on the internet.
Thank you!!
Is it right that for loop is actually using iterator and therefore I must wrap the for with synchronized?
There are two parts to your question.
Firstly, no, you're not using an iterator here, this is a basic for loop.
The enhanced for loop is the for loop which uses an iterator:
for (T element : list) { ... }
You can see in the language spec how this uses the iterator - search for where it says "The enhanced for statement is equivalent to a basic for statement of the form".
Secondly, even though you're not using an iterator, you do need synchronized. The two are orthogonal.
You are doing multiple operations (the size, the get and the add), with dependencies between them. You need to make sure that no other thread interferes with your logic:
the get depends on the size, since you don't want to try to get an element with index >= size, for instance;
the add depends on the get, since you're apparently trying to ensure the list elements are ordered. If another thread could sneak in and change the element after you get it, you might insert the new element in the wrong place.
You correctly avoid this potential interference this through synchronization over list, and creating the synchronizedList in such a way that nothing other than the synchronizedList can get direct access to the underlying list.
If your arg2.compareTo(arg) never return 0 (zero) you can use TreeSet. Will be much more simple:
set = Collections.synchronizedSet(new TreeSet<T>());
public void add(T arg) {
set.add(arg);
}
If you need hold same items (compareTo returns 0) then use the list:
list = new ArrayList<T>();
public void add(T arg) {
synchronized (list) {
int index = Collections.binarySearch(list, arg);
list.add(index, arg);
}
}
First and second cases complexity will be log(N) (10 for 1000 items). Your code complexity is N (1000 for 1000 items).

ConcurrentModificationException -> How can I change my code to stop it from throwing this error? [duplicate]

This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 5 years ago.
for (FPlayer p : fPlayer.getFaction().getOnline()) {
p.setFaction(null);
}
Basically, the getOnline method returns an array list of the current FPlayers that are online. When the FPlayer is removed from their faction, the faction is set to null (p.setFaction(null)).
I cannot think about how to change my code to stop it from throwing the ConcurrentModificationException. I have used an iterator but still, it.next().setFaction(null) still throws the same exception.
EDIT:
USING A LIST ITERATOR:
ListIterator<FPlayer> it = fPlayer.getFaction().getOnline().listIterator();
while (it.hasNext()) {
it.next().setFaction(null);
}
Caused by: java.util.ConcurrentModificationException
At the line
it.next().setFaction(null)
EDIT #2:
Set faction method:
public void setFaction(Faction faction) {
if (hasFaction()) {
this.faction.getOnline().remove(this);
}
this.faction = faction;
if (faction != null) {
this.faction.getOnline().add(this);
}
}
This is happening because while iterating you are removing the data from the list .
Couple of solutions .
If the list size is small convert it to array and then loop over
Use for loop for iteration .
for(int i=0;i<fPlayer.getFaction().getOnline().size();i++)
{
// Condition to check if true
if(true)
{
fPlayer.getFaction().getOnline().remove(i);
i--;
}
}
Yes, change your code so it doesn't change the collection inside the loop you are running. For example, create a copy of the collection before iterating.
for (Foo foo : new ArrayList(myFoos)) {
if (foo.isBar()) {
myFoos.remove(foo);
}
}
Iterating and changing the list without the new ArrayList() would have caused a ConcurrentModificationException

Add List Throws java.lang.UnsupportedOperationException [duplicate]

This question already has answers here:
Java List.add() UnsupportedOperationException
(8 answers)
Closed 5 years ago.
I have 4 functions with same get...() method, and I only change the name or identifier, but I've got different result with the fourth which is when to adding new item to list, it throws java.lang.UnsupportedOperationException. I assure you, I've already double checked all 4 functions and their relations, but have no idea why the fourth function like this.
public List<PropertyAttribute> getAttributes() {
if (selectedCode == null)
return null;
Criteria criteria = this.propertyAttributeDAO.createCriteria();
FilterUtils.byField(criteria, "propertyCode", this.selectedCode, true);
List<PropertyAttribute> list = criteria.list();
if (isNewAttribute()) {
list.add(0, this.curAttribute); //this line that throws exception
}
return list;
}
UPDATE STACK TRACE:
Caused by: java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at bos.web.pages.instrument.ViewProperty.getAttributes(ViewProperty.java:654)
at $InternalPropertyConduit_237587e282284.get(Unknown Source)
at org.apache.tapestry5.internal.bindings.PropBinding.get(PropBinding.java:59)
Unless it's clearly documented, don't assume it's safe to modify a list you received from another method. Even if it doesn't throw an exception, it may be altering critical state (if it's not implemented safely). Copy it to a new list and you can do whatever you like:
List<PropertyAttribute> list = new ArrayList<>(criteria.list());

Java ArrayList collection to hold latest 5 additions [duplicate]

This question already has answers here:
Fixed size queue which automatically dequeues old values upon new enqueues
(15 answers)
Closed 8 years ago.
I have an extra ordinary requirement of having a Java object array list (or list initialized as an ArrayList) which will be continuously updated and is supposed to hold only the latest 5 items with the objects to be sorted in a descending order of they were added.
How could this be accomplished? I need to come up with a solution within Java SE 7 preferably using no 3rd party library.
NOTE For those who mark this question as a duplicate, you do not seem to understand the requirement of using ArrayList for this case and that Queues and ArrayLists are different object types as well as Java and C# are different languages. Do you think Stackoverflow's internal search engine is not as good as you are in locating the duplicate questions?
When marking a question as "duplicate", make sure you link a true duplicate, please.
Not sure about the descending order, but this shows one way of fixing the size
class BoundedArrayList<E> extends ArrayList<E> {
private int fixedCapacity;
public BoundedArrayList(int fixedCapacity) {
super(fixedCapacity);
this.fixedCapacity = fixedCapacity;
}
#Override
public boolean add(E e) {
// is it about to cross limit ? removing it in advance
if(this.size() > fixedCapacity - 1) {
E element = this.remove(fixedCapacity - 1);
System.out.println("Removed due to overflow : " + element);
}
this.add(0, e);
return true;
}
public static void main(String[] args) {
BoundedArrayList<Integer> list = new BoundedArrayList<Integer>(5);
for(int i =0 ; i < 10; i++) {
list.add(i);
System.out.println(list);
}
}
}

how to remove list element

I am trying to remove list element but get this exception
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:161)
at apollo.exercises.ch08_collections.Ex4_RemoveOdd.removeOdd(Ex4_RemoveOdd.java:25)
at apollo.exercises.ch08_collections.Ex4_RemoveOdd.main(Ex4_RemoveOdd.java:15)
here is my code
public class Ex4_RemoveOdd {
removeOdd(Arrays.asList(1,2,3,5,8,13,21));
removeOdd(Arrays.asList(7,34,2,3,4,62,3));
public static void removeOdd(List<Integer> x){
for(int i=0;i<=x.size()-1;i++){
if (x.get(i)%2==0){
System.out.println(x.get(i));
}else{
x.remove(i);
}
}
}
}
So I make new class just to try to remove the element
public static void main(String[] args) {
List<Integer> x = Arrays.asList(1,2,3,5,8,13,21);
x.remove(1);
}
but still have error
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:161)
at apollo.exercises.ch08_collections.Ex4_RemoveOdd.main(Ex4_RemoveOdd.java:14)
FYI:I try to solve this excercise https://github.com/thecodepath/intro_java_exercises/blob/master/src/apollo/exercises/ch08_collections/Ex4_RemoveOdd.java
Arrays.asList returns a fixed-size list. Any call that will try to modify its size (by adding or removing elements) will throw this exception.
Use the ArrayList constructor that takes a collection as parameter instead.
removeOdd(new ArrayList<>(Arrays.asList(1,2,3,5,8,13,21)));
Also as noted in comments, it's safer (and highly recommended) to use the List's iterator to remove the elements from it.
Currently with your for-loop approach you will skip elements that you want to remove. For example when calling the method with the list [1,2,3,5,8,13,21], the first iteration will remove 1 so all the elements will be shifted by one in the list. Then the value of i is 1, the size of the list is 6 and list.get(1) will return 3 not 2 and so on.
At the end you will get [2, 5, 8, 21], which is not what you want.
If you are using java-8, your code could be simplified as
public static void removeOdd(List<Integer> x){
x.removeIf(i -> i%2 != 0);
}

Categories

Resources