-
Enhancement
-
Resolution: Unresolved
-
P5
-
None
-
None
The use of synchronization and Object.wait() is notoriously error-prone. It must test a condition in a loop, and it must catch InterruptedException. For this reason, most code gets this wrong. It would be nice to have a convenience method that provides correct and typical handling for these cases. The desired semantics might be:
- take the lock on an object
- while the predicate is not true, call wait()
- when the predicate is true, return true
- if wait() is interrupted, return false
This could be added to java.util.Objects, and the implementation might look something like the following:
public static <T> boolean await(T obj, Predicate<? super T> pred) {
Objects.requireNonNull(obj);
Objects.requireNonNull(pred);
try {
synchronized (obj) {
while (! pred.test(obj)) {
obj.wait();
}
return true;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return false;
}
}
For example, consider code like this that locks/waits/notifies a non-thread-safe data structure "queue".
synchronized (queue) {
while (queue.size() == 0) {
try {
queue.wait();
} catch (InterruptedException e) {
}
}
item = queue.remove(0);
}
process(item);
(This is adapted from the RMI TCPChannel.java code.) Note the mishandling of InterruptedException. This could be rewritten as follows:
if (Objects.await(queue, q -> q.size() > 0)) {
process(queue.remove(0));
} else {
// interrupted: return, break out of loop, etc.
}
BUT NOTE: this is incorrect. The removal from the queue is performed outside the lock! Clearly further design work is necessary; perhaps another lambda -- a Consumer<T> -- could be passed as well, and this would be performed while the lock is held after the predicate is true.
A common use case would be to assign something to a local variable, and this isn't possible from within a lambda. Perhaps the return value can be used for this, instead.
Variations might include timeout handling and throwing of InterruptedException.
- take the lock on an object
- while the predicate is not true, call wait()
- when the predicate is true, return true
- if wait() is interrupted, return false
This could be added to java.util.Objects, and the implementation might look something like the following:
public static <T> boolean await(T obj, Predicate<? super T> pred) {
Objects.requireNonNull(obj);
Objects.requireNonNull(pred);
try {
synchronized (obj) {
while (! pred.test(obj)) {
obj.wait();
}
return true;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return false;
}
}
For example, consider code like this that locks/waits/notifies a non-thread-safe data structure "queue".
synchronized (queue) {
while (queue.size() == 0) {
try {
queue.wait();
} catch (InterruptedException e) {
}
}
item = queue.remove(0);
}
process(item);
(This is adapted from the RMI TCPChannel.java code.) Note the mishandling of InterruptedException. This could be rewritten as follows:
if (Objects.await(queue, q -> q.size() > 0)) {
process(queue.remove(0));
} else {
// interrupted: return, break out of loop, etc.
}
BUT NOTE: this is incorrect. The removal from the queue is performed outside the lock! Clearly further design work is necessary; perhaps another lambda -- a Consumer<T> -- could be passed as well, and this would be performed while the lock is held after the predicate is true.
A common use case would be to assign something to a local variable, and this isn't possible from within a lambda. Perhaps the return value can be used for this, instead.
Variations might include timeout handling and throwing of InterruptedException.