Here's an excerpt from Java Concurrency in Practice:
public class DelegatingVehicleTracker {
private final ConcurrentMap<String, Point> locations;
private final Map<String, Point> unmodifiableMap;
public DelegatingVehicleTracker(final Map<String, Point> points) {
this.locations = new ConcurrentHashMap<>(points);
this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
}
public Map<String, Point> getLocations() {
return unmodifiableMap;
}
public Point getLocation(final String id) {
return locations.get(id);
}
public void setLocation(final String id, final int x, final int y) {
if (null == locations.replace(id, new Point(x, y))) {
throw new IllegalArgumentException("Invalid vehicle name: " + id);
}
}
}
My question is about the setLocation method which uses ConcurrentMap.replace. The JavaDoc of this method says that it's equivalent to:
if (map.containsKey(key)) {
return map.put(key, value);
} else return null;
except that the action is performed atomically.
What could go wrong if we don't use the atomic version. One possibility is that one thread sees that the map contains a given key and before it puts a new value for that key, another thread removes that key-value pair, but since the class in the example does not allow removals, this cannot happen.
Another possibility is that two threads attempt to replace the same key with different values. In that case one thread may not return the correct previous value but in the example we don't care about the previous value, the method setLocation returns void.
So, it seems as though the method could be rewritten without replace. And that is what prompted my question. In a subsequent version of the same class in the book, which almost identical to the one above, the method setLocation does not use replace, just containsKey and I was wondering if this could compromise thread safety.
the method setLocation does not use replace, just containsKey and I
was wondering if this could compromise thread safety.
It does, you've described it perfectly
What could go wrong if we don't use the atomic version. One
possibility is that one thread sees that the map contains a given key
and before it puts a new value for that key, another thread removes
that key-value pair, but since the class in the example does not allow
removals, this cannot happen.
That's why the implementation of ConcurrentHashMap.replace locks the node it tries to replace
/**
* Implementation for the four public remove/replace methods:
* Replaces node value with v, conditional upon match of cv if
* non-null. If resulting value is null, delete.
*/
final V replaceNode(Object key, V value, Object cv) {
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
boolean validated = false;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
validated = true;
for (Node<K,V> e = f, pred = null;;) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
if (value != null)
e.val = value;
else if (pred != null)
pred.next = e.next;
else
setTabAt(tab, i, e.next);
}
break;
}
pred = e;
if ((e = e.next) == null)
break;
}
}
else if (f instanceof TreeBin) {
validated = true;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(hash, key, null)) != null) {
V pv = p.val;
if (cv == null || cv == pv ||
(pv != null && cv.equals(pv))) {
oldVal = pv;
if (value != null)
p.val = value;
else if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (validated) {
if (oldVal != null) {
if (value == null)
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}
What could go wrong if we don't use the atomic version
Nothing.
It's stylistic. You could implement the setLocation method whatever way you want, but it just so happens that replace is a nice way to ensure that you only insert if the given location exists in the map.
In a subsequent version of the same class in the book, which almost identical to the one above, the method setLocation does not use replace, just containsKey and I was wondering if this could compromise thread safety.
It does not compromise thread safety. The assertion that the key is contained in the map cannot change in a concurrent write because given that the key is present, it will not suddenly be removed, and given that it is not present, nothing will be done.
The author decided to use contains key because the second example uses a mutable point rather than an immutable point. Recall that JCIP was written for Java 5 and such methods as computeIfPresent did not exist at the time. Therefore, the author must obtain the object it self in order to modify it. Thread safety will therefore be delegated to the mutable point rather than the vehicle tracker itself.
Related
I have to solve one problem, I don't know the reason why my code doesn't work.
I have to check if two lists I created are completely equals so they have the same value at the same position.
I'm allowed to use loops as well, even by I prefer the recursive mode.
Thank you so much for your help and time!
public static boolean checkEquality(Node n, Node m) {
if(n != null && m != null) {
boolean res = false;
while(n!=null) {
if(n.getElem()==m.getElem()) {
n = n.getNext();
m = m.getNext();
res = true;
}
else
{
res = false;
}
}
return res;
}
else
{
System.out.println("Lists empty!");
return true;
}
}
There are a couple of weak spots, so I give the solid solution:
public static boolean checkEquality(Node n, Node m) {
while (n != null && m != null) {
//if (!Objects.equals(n.getElem(), m.getElem())) {
if (n.getElem() != m.getElem()) {
return false;
}
n = n.getNext();
m = m.getNext();
}
return n == null && m == null;
}
Comparing can only be done while both n and m are not null. Your code only checks n.
== is not valid for instance for String. Instead of .equals one might also use Objects.equals which also tests for null.
getNext in every loop step.
two empty lists are also equal. Both lists should end at the same time.
The tst fails as soon as two compared nodes are not equal. So one should start with assuming a true result. And as soon as the comparison fails, one should no longer loop and certainly not overwrite res from false to true.
it would help if you elaborate what type of list u are comparing,
linkedlist or arrays. based on your function, it seems that you are planning to compare a linkedlist.
linkedlist documentation
arrays documentation
// sample comparison
boolean areIdentical(Node a_head, Node b_head) {
Node a = a_head, b = b_head;
while (a != null && b != null) {
if (a.data != b.data)
return false;
/* If we reach here, then a and b are not null
and their data is same, so move to next nodes
in both lists */
a = a.next;
b = b.next;
}
// If linked lists are identical, then 'a' and 'b'
// must be null at this point.
return (a == null && b == null);
}
I wonder following things
Doesn't a memory leak occur even if sycronized is not implemented in the get method?
If a memory leak occurs, what is the reason?
step1: put key : "a" -> value : "b"
step2: thread1 -> remove("a"), thread2 -> get("a") (occurs at the same time)
=> will the cache class still refer to "b"? can't "b" be garbage collected forever?
class Cache {
private Map<String, String> cache = new LinkedHashMap<>();
public synchronized void put(String key, String value) {
cache.put(key, value);
}
public synchronized void remove(String key) {
cache.remove(key);
}
public String get(String key) {
return cache.get(key);
}
}
I found the answer myself, I'm writing it down to share.
Below code is part of LinkedHashMap
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
In the case of LinkedHashMap, if you do not use synchronized, problems may arise if you do not use synchronized because the reference values of before and after are changed when reading.
In fact, I was wondering if there is a problem when reading from various types of Maps, and I wondered if there could be a memory leak problem in reading depending on the implementation of Map.
I want to delete a specific node using a key from a Ternary Search Tree. This works pretty well in most cases, but in some of my test sets, nodes that do not have a middle child also do not store values, which shouldn't happen.
I tried different methods I found online, but pretty much all of those few leave the tree in a dirty state, which makes searching a hassle, since you need to check if the leaf you found actually has a value, which shouldn't happen.
Here is my relevant code
private boolean hasChildren(Node x) {
return (x.left != null || x.mid != null || x.right != null);
}
private void reHang(Node x) {
if (hasChildren(x)) {
if (x.left != null) {
x.parent.mid = x.left;
x.left.right = x.right;
} else if (x.right != null) {
x.parent.mid = x.right;
}
}
}
private boolean remove(Node x, String key, int d) {
if (x == null) return false;
char c = key.charAt(d);
if (c < x.c) {
if (!remove(x.left, key, d)) {
x.left = null;
}
} else if (c > x.c) {
if (!remove(x.right, key, d)) {
x.right = null;
}
} else if (d < key.length() - 1) {
if (!remove(x.mid, key, d + 1)) {
x.mid = null;
if (x.val != null) return true;
}
} else {
x.val = null;
}
reHang(x);
return hasChildren(x);
}
private class Node
{
private Value val;
private char c;
private Node left, mid, right, parent;
}
Specifically, problems occur in this function I use for prefix lookups:
private Node getMidPath(Node x) {
if (x.left != null) return getMidPath(x.left);
if (x.mid == null) return x;
return getMidPath(x.mid);
}
Every Node that does not have a x.mid should have an x.val set, which is not always the case after remove, meaning I have dirty nodes.
Any help would be greatly appreciated.
You must see a ternary tree at was it is: Some nested Binary tree but witch each level have only on character as key instead of the whole string. Go down you binary trees until you string is exhausted. Here you will have two eventually references one to the actual data and and one two the actual subtree for longer string with same prefix. Now set the data to null. When the subtree reference is null remove the node. When now the entire subtree is empty continue with the subtree one character earlier. Here set subtree reference to null. Now is both references are null remove the node. When now the entire subtree is empty continue with the subtree one character earlier. And so on
Here is my test class..
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
A a = new A(0, 1);
HashMap<A, Integer> map = new HashMap<A, Integer>();
map.put(a, (a.x + a.y));
System.out.println(map.containsKey(a));
System.out.println("----------------- ");
System.out.println(map.containsKey(new A(0, 1)));
}
}
and here is my class A with hashcode and equal method generated by eclipse.
class A {
int x, y;
public A(int x, int y) {
super();
this.x = x;
this.y = y;
}
#Override
public int hashCode() {
System.out.println(" in hashcode");
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
#Override
public boolean equals(Object obj) {
System.out.println(" in equal");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
A other = (A) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
The output of program is
in hashcode
in hashcode
true
-----------------
in hashcode
in equal
true
My questions are: (I know the contract of hashcode and equal and why it is used)
Why in first case hashcode method is called twise ?
Why in first case equal does not called ? How JVM know that it is the same variable we are searching?
1) getHashCode is called one when you call put, then again when you call contains.
2) in the first case, the hashmap contains a reference to a, i.e. the address of a in memory, so there is no need to call equals. In the second case, the table lookup finds a, but this is a different object from the new A that you gave as a parameter, so there is a need to call equals() to find out if they are equal (they could be different and have the same hash code, this would be a collision).
If you take a look at the source code of HashMap.containsKey you'll find the following (taken from here: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java#HashMap.containsKey%28java.lang.Object%29 )
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
The important part is this (k = e.key) == key || (key != null && key.equals(k)). The metod first compares key objects by reference and then uses equals method for comparison but only if the references are different.
In your first call to containsKey key references will be the same (the same object). In second call references will be different (different instances of an "equal" object), hence call to equals.
As you can view in #kresimir-nesek pasted code,
First get the hashCode: int hash = (key == null) ? 0 : hash(key.hashCode()); and prints " in hashcode"
Then compare the java object identifier, (k = e.key) == key
1) in the first case is the same and return true. Calling to map.put you get the second " in hashcode"
2) but in the second case the object identifier is different, so call to equals (...(key != null && key.equals(k))...)
" in equal"
I need to check if all values in a map are equal. I have a method to perform this task but would like to use a library or native methods. Limitations: Java 5 + Apache Commons libraries.
public static boolean isUnique(Map<Dboid,?> aMap){
boolean isUnique = true;
Object currValue = null;
int iteration = 0;
Iterator<?> it = aMap.entrySet().iterator();
while(it.hasNext() && isUnique){
iteration++;
Object value = it.next();
if(iteration > 1){
if (value != null && currValue == null ||
value == null && currValue != null ||
value != null && currValue != null & !value.equals(currValue)) {
isUnique = false;
}
}
currValue = value;
}
return isUnique;
}
What about this something like this:
Set<String> values = new HashSet<String>(aMap.values());
boolean isUnique = values.size() == 1;
how about
return (new HashSet(aMap.values()).size() == 1)
I know the original questions asks for solutions in Java 5, but in case someone else searching for an answer to this question is not limited to Java 5 here is a Java 8 approach.
return aMap.values().stream().distinct().limit(2).count() < 2
You could store the values in a Bidirectional Map and always have this property.
public static boolean isUnique(Map<Dboid,?> aMap) {
Set<Object> values = new HashSet<Object>();
for (Map.Entry<Dboid,?> entry : aMap.entrySet()) {
if (!values.isEmpty() && values.add(entry.getValue())) {
return false;
}
}
return true;
}
This solution has the advantage to offer a memory-saving short cut if there are many differences in the map. For the special case of an empty Map you might choose false as return value, change it appropriately for your purpose.
Or even better without a Set (if your Map does not contain null-values):
public static boolean isUnique(Map<Dboid,?> aMap) {
Object value = null;
for (Object entry : aMap.values()) {
if (value == null) {
value = entry;
} else if (!value.equals(entry)) {
return false;
}
}
return true;
}
As my comment above:
//think in a more proper name isAllValuesAreUnique for example
public static boolean isUnique(Map<Dboid,?> aMap){
if(aMap == null)
return true; // or throw IlegalArgumentException()
Collection<?> c = aMap.getValues();
return new HashSet<>(c).size() <= 1;
}