Clearly, notify
wakes (any) one thread in the wait set, notifyAll
wakes all threads in the waiting set. The following discussion should clear up any doubts. notifyAll
should be used most of the time. If you are not sure which to use, then use notifyAll
.Please see explanation that follows.
Read very carefully and understand. Please send me an email if you have any questions.
Look at producer/consumer (assumption is a ProducerConsumer class with two methods). IT IS BROKEN (because it uses notify
) - yes it MAY work - even most of the time, but it may also cause deadlock - we will see why:
public synchronized void put(Object o) {
while (buf.size()==MAX_SIZE) {
wait(); // called if the buffer is full (try/catch removed for brevity)
}
buf.add(o);
notify(); // called in case there are any getters or putters waiting
}
public synchronized Object get() {
// Y: this is where C2 tries to acquire the lock (i.e. at the beginning of the method)
while (buf.size()==0) {
wait(); // called if the buffer is empty (try/catch removed for brevity)
// X: this is where C1 tries to re-acquire the lock (see below)
}
Object o = buf.remove(0);
notify(); // called if there are any getters or putters waiting
return o;
}
FIRSTLY,
Why do we need a while loop surrounding the wait?
We need a while
loop in case we get this situation:
Consumer 1 (C1) enter the synchronized block and the buffer is empty, so C1 is put in the wait set (via the wait
call). Consumer 2 (C2) is about to enter the synchronized method (at point Y above), but Producer P1 puts an object in the buffer, and subsequently calls notify
. The only waiting thread is C1, so it is woken and now attempts to re-acquire the object lock at point X (above).
Now C1 and C2 are attempting to acquire the synchronization lock. One of them (nondeterministically) is chosen and enters the method, the other is blocked (not waiting - but blocked, trying to acquire the lock on the method). Let's say C2 gets the lock first. C1 is still blocking (trying to acquire the lock at X). C2 completes the method and releases the lock. Now, C1 acquires the lock. Guess what, lucky we have a while
loop, because, C1 performs the loop check (guard) and is prevented from removing a non-existent element from the buffer (C2 already got it!). If we didn't have a while
, we would get an IndexArrayOutOfBoundsException
as C1 tries to remove the first element from the buffer!
NOW,
Ok, now why do we need notifyAll?
In the producer/consumer example above it looks like we can get away with notify
. It seems this way, because we can prove that the guards on the wait loops for producer and consumer are mutually exclusive. That is, it looks like we cannot have a thread waiting in the put
method as well as the get
method, because, for that to be true, then the following would have to be true:
buf.size() == 0 AND buf.size() == MAX_SIZE
(assume MAX_SIZE is not 0)
HOWEVER, this is not good enough, we NEED to use notifyAll
. Let's see why ...
Assume we have a buffer of size 1 (to make the example easy to follow). The following steps lead us to deadlock. Note that ANYTIME a thread is woken with notify, it can be non-deterministically selected by the JVM - that is any waiting thread can be woken. Also note that when multiple threads are blocking on entry to a method (i.e. trying to acquire a lock), the order of acquisition can be non-deterministic. Remember also that a thread can only be in one of the methods at any one time - the synchronized methods allow only one thread to be executing (i.e. holding the lock of) any (synchronized) methods in the class. If the following sequence of events occurs - deadlock results:
STEP 1:
- P1 puts 1 char into the buffer
STEP 2:
- P2 attempts put
- checks wait loop - already a char - waits
STEP 3:
- P3 attempts put
- checks wait loop - already a char - waits
STEP 4:
- C1 attempts to get 1 char
- C2 attempts to get 1 char - blocks on entry to the get
method
- C3 attempts to get 1 char - blocks on entry to the get
method
STEP 5:
- C1 is executing the get
method - gets the char, calls notify
, exits method
- The notify
wakes up P2
- BUT, C2 enters method before P2 can (P2 must reacquire the lock), so P2 blocks on entry to the put
method
- C2 checks wait loop, no more chars in buffer, so waits
- C3 enters method after C2, but before P2, checks wait loop, no more chars in buffer, so waits
STEP 6:
- NOW: there is P3, C2, and C3 waiting!
- Finally P2 acquires the lock, puts a char in the buffer, calls notify, exits method
STEP 7:
- P2's notification wakes P3 (remember any thread can be woken)
- P3 checks the wait loop condition, there is already a char in the buffer, so waits.
- NO MORE THREADS TO CALL NOTIFY and THREE THREADS PERMANENTLY SUSPENDED!
SOLUTION: Replace notify
with notifyAll
in the producer/consumer code (above).
The transient
keyword in Java is used to indicate that a field should not be part of the serialization (which means saved, like to a file) process.
From the Java Language Specification, Java SE 7 Edition, Section 8.3.1.3. transient
Fields:
Variables may be marked transient
to
indicate that they are not part of the
persistent state of an object.
For example, you may have fields that are derived from other fields, and should only be done so programmatically, rather than having the state be persisted via serialization.
Here's a GalleryImage
class which contains an image and a thumbnail derived from the image:
class GalleryImage implements Serializable
{
private Image image;
private transient Image thumbnailImage;
private void generateThumbnail()
{
// Generate thumbnail.
}
private void readObject(ObjectInputStream inputStream)
throws IOException, ClassNotFoundException
{
inputStream.defaultReadObject();
generateThumbnail();
}
}
In this example, the thumbnailImage
is a thumbnail image that is generated by invoking the generateThumbnail
method.
The thumbnailImage
field is marked as transient
, so only the original image
is serialized rather than persisting both the original image and the thumbnail image. This means that less storage would be needed to save the serialized object. (Of course, this may or may not be desirable depending on the requirements of the system -- this is just an example.)
At the time of deserialization, the readObject
method is called to perform any operations necessary to restore the state of the object back to the state at which the serialization occurred. Here, the thumbnail needs to be generated, so the readObject
method is overridden so that the thumbnail will be generated by calling the generateThumbnail
method.
For additional information, the article Discover the secrets of the Java Serialization API (which was originally available on the Sun Developer Network) has a section which discusses the use of and presents a scenario where the transient
keyword is used to prevent serialization of certain fields.
Best Answer
Well, it does mean that every object has to potentially have a monitor associated with it. The same monitor is used for
synchronized
. If you agree with the decision to be able to synchronize on any object, thenwait()
andnotify()
don't add any more per-object state. The JVM may allocate the actual monitor lazily (I know .NET does) but there has to be some storage space available to say which monitor is associated with the object. Admittedly it's possible that this is a very small amount (e.g. 3 bytes) which wouldn't actually save any memory anyway due to padding of the rest of the object overhead - you'd have to look at how each individual JVM handled memory to say for sure.Note that just having extra methods doesn't affect performance (other than very slightly due to the code obvious being present somewhere). It's not like each object or even each type has its own copy of the code for
wait()
andnotify()
. Depending on how the vtables work, each type may end up with an extra vtable entry for each inherited method - but that's still only on a per type basis, not a per object basis. That's basically going to get lost in the noise compared with the bulk of the storage which is for the actual objects themselves.Personally, I feel that both .NET and Java made a mistake by associating a monitor with every object - I'd rather have explicit synchronization objects instead. I wrote a bit more on this in a blog post about redesigning java.lang.Object/System.Object.