On page 65 and 66 of Java Concurrency in Practice Brian Goetz lists the following code:
@ThreadSafe
public class DelegatingVehicleTracker {
private final Con
I think your confusion is due to misunderstanding of Collections.unmodifiableMap
. Direct mutation of the map returned by Collections.unmodifiableMap
is not allowed, however, mutating the backing map is totally fine (as long as the backing map allows mutation). For example:
Map<String,String> map = new HashMap<>();
Map<String, String> unmodifiableMap = Collections.unmodifiableMap(map);
map.put("key","value");
for (String key : unmodifiableMap.keySet()) {
System.out.println(key); // prints key
}
So, unmodifiableMap
in the DelegatingVehicleTracker
example is backed by a mutable map locations
(a thread-safe
one). setLocation
mutates locations
atomically and hence changes will be visible for threads holding references to the unmodifiableMap
knowing that those thread can't mutate the unmodifiableMap
.
Readers don't have access to locations
so mutating it will be done through DelegatingVehicleTracker
only and hence the name delegation
.
In what sense would Thread A's unmodifiableMap be "live"? I do not see how changes made by Thread B via calls to setLocation() would be reflected in Thread A's unmodifiableMap
This is because getLocations()
returns an unmodifiable wrapped map of the actual mutable map.
public DelegatingVehicleTracker(Map<String, Point> points) {
locations = new ConcurrentHashMap<String, Point>(points);
unmodifiableMap = Collections.unmodifiableMap(locations);
}
...
public Map<String, Point> getLocations() {
return unmodifiableMap;
}
So any changes later will be automatically reflected in the original returned map since they both eventually point to the same internal Map object.
Goetz goes on to say that getLocationsAsStatic() could be called were an "unchanging view of the fleet required"
This code
public Map<String, Point> getLocationsAsStatic() {
return Collections.unmodifiableMap(
new HashMap<String, Point>(locations));
}
is static in the sense that future changes to locations
are not reflected since it returns a new map with a copy of the all the current key-value pairs.
getLocations()
will return a read-only map which will reflect updates after getLocations()
is called.
getLocationsAsStatic()
on the other hand, will return a read-only-snapshot (aka deep copy) of the Location map at the time getLocationsAsStatic()
is called.
To illustrate:
Map<String, Point> locs = // a map with point A(1,1) in it
DelegatingVehicleTracker tracker = DelegatingVehicleTracker(locs);
Map<String, Point> snapshot = getLocationsAsStatic();
Map<String, Point> live = getLocations();
Point newB = // a point A(2,2)
tracker.setLocation(newB);
snapshot.get("A"); // will read A(1,1)
live.get("A"); // will read A(2,2)