EN
Java - create thread safe ArrayList (Collections.synchronizedList)
7 points
In this article, we would like to show you how to create thread safe ArrayList in java.
Quick solution:
xxxxxxxxxx
1
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Full example with imports:
xxxxxxxxxx
1
import java.util.ArrayList;
2
import java.util.Arrays;
3
import java.util.Collections;
4
import java.util.List;
5
6
public class Example {
7
8
public static void main(String[] args) {
9
10
List<String> results = Collections.synchronizedList(new ArrayList<>());
11
12
// now we can safely use this list between different threads
13
// as all methods of this list are synchronized
14
15
results.add("A");
16
results.addAll(Arrays.asList("B", "C"));
17
18
}
19
}
When iterating the iterator must be synched by programmer.
xxxxxxxxxx
1
import java.util.ArrayList;
2
import java.util.Arrays;
3
import java.util.Collections;
4
import java.util.Iterator;
5
import java.util.List;
6
7
public class Example {
8
9
public static void main(String[] args) {
10
11
List<String> results = Collections.synchronizedList(new ArrayList<>());
12
13
// now we can safely use this list between different threads
14
// as all methods of this list are synchronized
15
16
results.add("A");
17
results.addAll(Arrays.asList("B", "C"));
18
19
synchronized (results) {
20
Iterator<String> iterator = results.iterator(); // Must be in synchronized block
21
while (iterator.hasNext()) {
22
System.out.println(iterator.next());
23
24
// other operations
25
26
}
27
}
28
}
29
}
Let's take a look at java docs:
xxxxxxxxxx
1
// It is imperative that the user manually synchronize on the returned list when iterating over it:
2
List list = Collections.synchronizedList(new ArrayList());
3
synchronized (list) {
4
Iterator i = list.iterator(); // Must be in synchronized block
5
while (i.hasNext())
6
foo(i.next());
7
}
We can take a look at implementation of SynchronizedList
:
xxxxxxxxxx
1
public ListIterator<E> listIterator() {
2
return list.listIterator(); // Must be manually synched by user
3
}
4
5
public ListIterator<E> listIterator(int index) {
6
return list.listIterator(index); // Must be manually synched by user
7
}
When we enter Collections.synchronizedList(new ArrayList<>())
method, we will see that all methods of list are wrapped by synchronized.
xxxxxxxxxx
1
// entire synchronization is on below lock / mutex / mutual exclusion:
2
final Object mutex; // Object on which to synchronize
We can read more about lock / mutex here:
Java Collections synchronizedList implementation (java 11 corretto):
xxxxxxxxxx
1
package java.util;
2
3
// ...
4
5
public class Collections {
6
7
// ...
8
9
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
10
private static final long serialVersionUID = 3053995032091335093L;
11
12
final Collection<E> c; // Backing Collection
13
final Object mutex; // Object on which to synchronize
14
15
// ...
16
17
}
18
19
// ...
20
21
public static <T> List<T> synchronizedList(List<T> list) {
22
return (list instanceof RandomAccess ?
23
new SynchronizedRandomAccessList<>(list) :
24
new SynchronizedList<>(list));
25
}
26
27
static <T> List<T> synchronizedList(List<T> list, Object mutex) {
28
return (list instanceof RandomAccess ?
29
new SynchronizedRandomAccessList<>(list, mutex) :
30
new SynchronizedList<>(list, mutex));
31
}
32
33
/**
34
* @serial include
35
*/
36
static class SynchronizedList<E>
37
extends SynchronizedCollection<E>
38
implements List<E> {
39
private static final long serialVersionUID = -7754090372962971524L;
40
41
final List<E> list;
42
43
SynchronizedList(List<E> list) {
44
super(list);
45
this.list = list;
46
}
47
SynchronizedList(List<E> list, Object mutex) {
48
super(list, mutex);
49
this.list = list;
50
}
51
52
public boolean equals(Object o) {
53
if (this == o)
54
return true;
55
synchronized (mutex) {return list.equals(o);}
56
}
57
public int hashCode() {
58
synchronized (mutex) {return list.hashCode();}
59
}
60
61
public E get(int index) {
62
synchronized (mutex) {return list.get(index);}
63
}
64
public E set(int index, E element) {
65
synchronized (mutex) {return list.set(index, element);}
66
}
67
public void add(int index, E element) {
68
synchronized (mutex) {list.add(index, element);}
69
}
70
public E remove(int index) {
71
synchronized (mutex) {return list.remove(index);}
72
}
73
74
public int indexOf(Object o) {
75
synchronized (mutex) {return list.indexOf(o);}
76
}
77
public int lastIndexOf(Object o) {
78
synchronized (mutex) {return list.lastIndexOf(o);}
79
}
80
81
public boolean addAll(int index, Collection<? extends E> c) {
82
synchronized (mutex) {return list.addAll(index, c);}
83
}
84
85
public ListIterator<E> listIterator() {
86
return list.listIterator(); // Must be manually synched by user
87
}
88
89
public ListIterator<E> listIterator(int index) {
90
return list.listIterator(index); // Must be manually synched by user
91
}
92
93
public List<E> subList(int fromIndex, int toIndex) {
94
synchronized (mutex) {
95
return new SynchronizedList<>(list.subList(fromIndex, toIndex),
96
mutex);
97
}
98
}
99
100
101
public void replaceAll(UnaryOperator<E> operator) {
102
synchronized (mutex) {list.replaceAll(operator);}
103
}
104
105
public void sort(Comparator<? super E> c) {
106
synchronized (mutex) {list.sort(c);}
107
}
108
109
/**
110
* SynchronizedRandomAccessList instances are serialized as
111
* SynchronizedList instances to allow them to be deserialized
112
* in pre-1.4 JREs (which do not have SynchronizedRandomAccessList).
113
* This method inverts the transformation. As a beneficial
114
* side-effect, it also grafts the RandomAccess marker onto
115
* SynchronizedList instances that were serialized in pre-1.4 JREs.
116
*
117
* Note: Unfortunately, SynchronizedRandomAccessList instances
118
* serialized in 1.4.1 and deserialized in 1.4 will become
119
* SynchronizedList instances, as this method was missing in 1.4.
120
*/
121
private Object readResolve() {
122
return (list instanceof RandomAccess
123
? new SynchronizedRandomAccessList<>(list)
124
: this);
125
}
126
}
127
128
/**
129
* @serial include
130
*/
131
static class SynchronizedRandomAccessList<E>
132
extends SynchronizedList<E>
133
implements RandomAccess {
134
135
SynchronizedRandomAccessList(List<E> list) {
136
super(list);
137
}
138
139
SynchronizedRandomAccessList(List<E> list, Object mutex) {
140
super(list, mutex);
141
}
142
143
public List<E> subList(int fromIndex, int toIndex) {
144
synchronized (mutex) {
145
return new SynchronizedRandomAccessList<>(
146
list.subList(fromIndex, toIndex), mutex);
147
}
148
}
149
150
private static final long serialVersionUID = 1530674583602358482L;
151
152
/**
153
* Allows instances to be deserialized in pre-1.4 JREs (which do
154
* not have SynchronizedRandomAccessList). SynchronizedList has
155
* a readResolve method that inverts this transformation upon
156
* deserialization.
157
*/
158
private Object writeReplace() {
159
return new SynchronizedList<>(list);
160
}
161
}
162
163
// ...
164
165
}