You have these options:
Collections.synchronizedList()
: you can wrap any List
implementation (ArrayList
, LinkedList
or a 3rd-party list). Access to every method (reading and writing) will be protected using synchronized
. When using iterator()
or enhanced for loop, you must manually synchronize the whole iteration. While iterating, other threads are fully blocked even from reading. You can also synchronize separately for each hasNext
and next
calls, but then ConcurrentModificationException
is possible.
CopyOnWriteArrayList
: it's expensive to modify, but wait-free to read. Iterators never throw ConcurrentModificationException
, they return a snapshot of the list at the moment of iterator creation even if the list is modified by another thread while iterating. Useful for infrequently updated lists. Bulk operations like addAll
are preferred for updates - the internal array is copied less many times.
Vector
: very much like synchronizedList(new ArrayList<>())
, but iteration is synchronized too. However, iterators can throw ConcurrentModificationException
if the vector is modified by another thread while iterating.
Other options:
Collections.unmodifiableList()
: lock-free, thread-safe, but non-modifiableList.of
& List.copyOf
: Another non-modifiable list in Java 9 and later.Queue
or Deque
might be an alternative if you only add/remove at the ends of the list and iterate the list. There's no access by index and no adding/removing at arbitrary places. They have multiple concurrent implementations with better performance and better concurrent access, but it's beyond the scope of this question. You can also have a look at JCTools, they contain more performant queue implementations specialized for single consumer or single producer.