0

I have been thinking of send a proposal to the Java language architects.

In a synchronized block

synchronized(lock) {

// If there is no notification before this point
// <--- implicitly put here // lock.notifyAll(); // OR // lock.notify();  
}

After a thread left synchronized block, it cannot call lock.notifyAll() / lock.notify() anymore without getting exception.

Forgetting to notify other thread monitor holders may forever make them (other threads) wait (unless, they put some timeout in their wait method).

synchronized(lock) {

     lock.wait(); //<--- this thread may forever freeze here
}

I cannot imagine a situation in which such behavior (inserting implicit notification at the end of a synchronized block, when there is no explicit notification) is undesirable.

The same approach can be applied to synchronized methods.


There can be different ways how to (technically) implement such behavior, for example:

@autonotify
synchronized(lock) {
...
}

@autonotify
public void synchronized doSomething() {
...
}

Or:

@autonotifyAll
synchronized(lock) {
...
}

@autonotifyAll
public void synchronized doSomething() {
...
}

Or - make auto-notification the default behavior, but leaving ability to suppress it, for example:

@suppressautonotify
synchronized(lock) {
...
}

@suppressautonotifyAll
public void synchronized doSomething() {
...
}

What do you think? Objections?

The best commentary for or against the proposal will be accepted as the answer.

Alex Kreutznaer
  • 1,170
  • 8
  • 18
  • There are a lot of cases where doing this would result in bugs (or at least, unneeded wakeups when the wait condition hasn't been fulfilled). Not all synchronization is done to notify the monitor. – Mark Rotteveel Apr 08 '13 at 19:11

2 Answers2

8

Doing it automatically or by default is a big no-no. There are many situations where you synchronize on a lock without wanting to notify at the end of the synchronized block. Doing so would break a whole lot of existing programs.

And why do it with @autonotifyAll instead of doing it with a simple lock.notifyAll() at the end of the synchronized block. If you forget to call lock.notifyAll(), you have as many chances to forget @autonotifyAll. And it would make things less readable, and less consistent.

The best practice, anyway, is to avoid using these very low-level methods, and to use higher-level abstractions, like blocking queues, countdown latches, semaphores, etc.

If I had to decide, your suggestion would be rejected.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • That's what I thought, too - changing the behavior to auto-nofity will break existing applications. May be that's the main argument NOT to do it. – Alex Kreutznaer Apr 08 '13 at 19:17
  • But, the question still remains - can you imagine a situation when leaving a synchronized block without notify() or notifyAll() is justified? I cannot. – Alex Kreutznaer Apr 08 '13 at 19:18
  • 2
    Yes, in 99% of the cases, when you synchronize only to ensure a mutual exclusion. Look at the source of java.util.Vector for example. All its methods are synchronized, and there is zero call to notify or notifyAll inside. – JB Nizet Apr 08 '13 at 19:20
  • The difference between autonotifyAll and lock.notifyAll() is that if you throw an exception, lock.notifyAll() may be skipped, but autonotifyAll may be implemented in a way similar to finally block behavior. – Alex Kreutznaer Apr 08 '13 at 19:22
  • Well, looks like you are right 99%. The only thing I would still consider is autonotify/autonotifyAll annotations, with behavior similar to finally block execution. Or something like synchronized (lock) {} finally {}. – Alex Kreutznaer Apr 08 '13 at 19:27
  • @AlexKreutznaer I think the times I have used `wait()` and `notifyAll()` can be counted on one hand and in those cases I didn't want, nor need to `notifyAll()` every time I left a `synchronized` block, on the other hand I regularly use `synchronized`. – Mark Rotteveel Apr 08 '13 at 19:29
  • I also disagree with that. If you have an unexpected exception inside the synchronized block, notifying has a pretty good chance of being the wrong thing to do. Closing a file when you have finished reading it, even when an exception has happened, is a good thing because nothing bad can happen by closing a file, and it makes sure limited resources are released. But notifying all the threads when you have an unexpected exception is not the same thing. – JB Nizet Apr 08 '13 at 19:31
  • 1
    @AlexKreutznaer There already is a way to do synchronized(lock) {} finally {}. It's just very slightly different than the syntax you want: `synchronized(lock) { try {...} finally {...} }` – yshavit Apr 08 '13 at 19:31
  • @yshavit Agreed, and one could even argue that a `synchronized(lock) {..} finally { lock.notifyAll }` would be invalid as you already exited the `synchronized` block and therefor no longer hold the monitor (see try-with-resources, visibility of variables local to a block, etc. – Mark Rotteveel Apr 08 '13 at 19:46
  • @yshavit Following your logic introducing try(...get some resources...) was not necessary. Because old try {...} catch() {...} finally {...} was enough to do it. – Alex Kreutznaer Apr 08 '13 at 19:50
  • When I am talking about adding finally to synchronized it is rather about improving readability of code and convenience. Of course all that can be done using regular try-catch-finally inside the synchronized block. – Alex Kreutznaer Apr 08 '13 at 19:53
  • @AlexKreutznaer Fair enough. But the language designers felt that the try-with-resources pattern was common enough -- and mishandled enough due to limitations in the language -- that it was worth putting into the language. Reading a file is not considered arcane or difficult, so it makes sense to make it more accessible to the public. Low-level synchronization/wait/notify is something where, frankly, if you feel you need `@autonotify` you maybe shouldn't be doing it at all. – yshavit Apr 08 '13 at 20:03
  • Actually, an automatic `notifyAll()` should not break any well-formed program, as every well-formed program must be prepared to handle *Spurious Wakeups* which are always possible when performing `wait`. However, it could become a performance nightmare. – Holger Oct 19 '18 at 12:55
0

Object.wait and Object.notify/All are considered pretty low-level mechanisms for synchronization; most of the time, you'll want to use higher-level constructs like those found in java.util.concurrent or its sub-packages. If your use case is subtle or special enough that you need to use these Object methods rather than the higher-level tools that the JDK people have built, tested and optimized for you -- well then, you probably want full control, and hopefully (for the users of your code) you can handle it.

To use a driving metaphor, this question is the equivalent of buying a car with a manual transmission instead of an automatic, and then asking the car manufacturer to automatically work the clutch for you when you want to switch gears.

yshavit
  • 42,327
  • 7
  • 87
  • 124
  • 1
    Totally off topic, but a lot of cars on the market today have "paddle shifters" that do just that! ;-) (although admittedly they are not manual transmissions) – The111 Sep 15 '13 at 06:26