Can a list passed to a function be modofied by another thread in Java?

Integer getElement( List<Integer> list ) { int i = Random.getInt( list.size() ); return list.get( i ); } 

Question: although this function was called from a thread, is there a way that the list passed to this function can be changed by another thread?

+6
source share
6 answers

Not. java.util.List does not guarantee thread safety. The list can be changed between list.size() and list.get() another thread. In addition, the memory mismatch problem is also a problem.

I could think of three ways to solve it:

  • Use an immutable list. Collections.unmodifiableList (list) is excellent, Guava ImmuntableList is better.
  • Sync list. but you need to synchronize the list instance throughout the program.
  • List java.util.concurrent . This is a complex concurrency solution.
+6
source

Passing list to your function is a reference to the list object. If any other threads have links to the same list object, then this is not thread safe.

+8
source
 > Integer getElement( List<Integer> list ) { > > int i = Random.getInt( list.size() ); > > return list.get( i ); > > } 

Question: although this method being called from a thread, IS is there a way in which the list passed to this function can be changed by another thread?

(the first Java has no "functions", it has "methods")

It DEPENDS in the implementation of the list. The whole is immutable, and if you are implementing the List implementation, it is immutable and correctly implemented, then the list cannot be modified and is completely unsafe.

If the implementation of List is not immutable, then yes, the list can be modified by another thread.

An example of an immutable implementation of List is, in the excellent Google Guava API, ImmutableList, which extends the Java list and is completely thread safe. There are other examples of a fully immutable List implementation, and they are completely thread safe (and, of course, they are usually intended for use with fully thread safe objects, such as the fully immutable Integer class).

+2
source

Is the list passed to the function stream in Java?

The list passed to the function is the same as any other list. This is thread safe if the list is already thread safe or if no other thread can see the list.

+1
source

Is the list passed to the function stream in Java?

Question: while this function was called from a thread, is there a way that the list passed to this function can be changed by another thread?

This concerns thread safety, and in particular the list instance. It depends on the specific class of the list instance (this is both simultaneous behavior and mutability behavior). If this instance is mutable, it matters to the visibility / exposure of this list.

I changed your example in the hope of better understanding the issue and providing more specific solutions:

 private void method1 { final List<Integer> list = new ArrayList<Integer>(); list.add(Integer.valueOf(1)); // Add the hypothetical mutator from another Thread new Thread(new Runnable(){ public void run() { list.clear(); // if this where to happen between first & second access in getElement an IndexOutOfBoundsException would occur } }).start(); Integer someInt = getElement(list); } // this is not thread-safe private Integer getElement(List<Integer> list) { int len = list.size(); // first access to list int idx = Random.getInt(len); Integer retval = list.get(idx); // second access to list return retval; } 

Two ways:

  • parallel instances
  • <i> are unchanging

concurrent list instances:

  • java.util.concurrent.CopyOnWriteArrayList
  • java.util.Collections.synchronizedList ()

Based on the above code, here are a few options on how to make threadElement thread safe.

immutable

Best : make immutable list (so no sync needed)

 final List<Integer> list1 = Arrays.asList(1); // or Guava ImmutableList final List<Integer> list2 = ImmutableList.of(1); // the reference to the underlying mutable list is now hidden final List<Integer> list3 = getList(); private List<Integer> getList() { List<Integer> list = new ArrayList<Integer>(); list.add(1); // mutate the list return Collection.unmodifiableList(list); } 

Con: causes a new problem with the mutator. To solve this problem, an understanding of the whole problem will be required.

concurrent

1) Defensive copy in getElement () combined with concurrent instance.

 private Integer getElement(Collection<Integer> list) { final List<Integer> copy; synchronized (list) { // needed if Collections.synchronized*() copy = new ArrayList<Integer>(list); } return copy.get(Random.getInt(list.size())); } 

Con: performance for copying

2) Rewrite getElement () so that it uses only iterator with concurrent instance

 // This is just top-of-the-head, private <T> T getElement(Iterable<T> list) { synchronized(list) { // needed if Collections.synchronized*() for(T elem : list) { // not even distribution of results, look for better algorithm if (Random.nextBoolean()) { // this is the one return elem; } } } return getElement(list); // put in a better fail-safe than this } 

Con: current implementation does not provide uniform distribution


There is no easy way to solve concurrency.

-1
source

As others have explained, there is no way (or at least not very complicated) for getElement ensure that a List cannot be changed when a method accesses it. If another Thread resized to reduce its size smaller than the random int generated by the first line, then the second line will throw an IndexOutOfBoundsException .

The only option is to ensure that all threads accessing the list only synchronize access. I.e:

 Integer getElement( List<Integer> list ) { synchronized(list) { int i = Random.getInt( list.size() ); return list.get( i ); } } // some method that modifies the list void modify(List<Integer> list ) { synchronized(list) { .... } } 

Note that even if getElement , there is no guarantee that the item will still exist in the list when the caller uses this item. To ensure that callers also need to synchronize:

 // Thread 1 synchronized (list) { Integer someElement = getElement(list); use(someElement); // use can be outside the synchronized block if it is ok to use // an element that is no longer in the list } //Thread 2 synchronized (list) { modify(list); // call some function that modifies the list } 

A less error-prone approach is to implement the List class, which requires it to be locked before it is available. Usage will be as follows:

 try { list.lock(); Integer someElement = getElement(list); use(someElement); } finally { list.unlock(); } 

It looks like the previous approach (and will look even more with try-with-resources ), the difference is that the List get / set methods, etc. raise an exception if they are accessed without acquiring a lock.

EDIT: This of course only applies when using mutable lists. The scenario is completely different when using immutable lists, although you still need some kind of synchronization mechanism if the item should be on the list when it is in use.

-3
source

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


All Articles