Email address:
Password:
SCJP/OCPJP 1.6 certification
Practice Exams and Training
SCJP Online Training » Discussions

Question about synchronization primitives

Hi!

I know that "the method Object.wait() must be called within an appropriate synchronized context", also what is "under the hood" of this restriction?

Is this because of some condition you are waiting for? And because of that condition to be met correctly, we need to synchronize?


Thank you!

First, I must say that you don't need to go that deeper with wait() and notify() stuff for the exam.


Answering your question anyway, the simplest explanation would be: it's because wait() needs to release the lock, temporarily, so another thread can notify() upon it. Right after the first thread gets notified it needs to be able to reacquire the monitor lock of that object.


The reason behind this lock/release mechanism is to make sure the waiting thread won't be missing a notification call. Let's say you have block of code which, at a certain point of it requires a key to be pressed to continue. So in that code you first check if a key was pressed, and if it was not pressed, you wait until the key is pressed. In pseudo code:


print(" Press a key to continue... ");
if ( ! isKeyPressed ) {
    this.wait ();
}
do stuff based on the key pressed
// other codes


As you wait, another thread monitors the key pressed event, and once the key was pressed, the waiting object would be notified, so it can continue from line 5.


Now, when you do this, you should put the whole code (at least what's within line-2 to line-4) inside a synchronized block (which waits for the monitor lock of the object the thread is going to wait on).


print(" Press a key to continue... ");
synchronized(this) {
   if ( ! isKeyPressed ) {
      this.wait ();
   }
}
do stuff based on the key pressed
// other codes


At the same time, the statement notifying that object needs to be in a synchronized context too.


synchronized(this) {
   // monitor keypressed event
   if (keyPressed ) {
      this.notifyAll();
   }
}


Let's consider the above two pieces of codes where the synchronized keyword was used. If there was no such requirement to have such synchronized blocks, there's a chance that the user would press a key right after the waiting thread runs the line-3 (which checks if a key was pressed and realized not), but is yet to call line-4. As you press the key, the monitoring thread would call notifyAll(). After all that happens, the first thread reaches line-4, which waits for a key to be pressed. In other words, it has missed the notify() event, so it has to wait until the user press a key again!


To avoid that problem we require synchronizing on both notifying and waiting objects.

Thank you!

So the main idea is that we synchronize the state of the object we are using as a monitor.

Hmm, it sounds like something I can't interpret correctly. What do you mean by the state of the object?

I mean that "isKeyPressed" flag - it is a property inside our class, what all its objects have. And this property can be updated by different threads and it serves like a synchronization mechanism. As this state can be changed by the "outer world" and as it used like a flag, it must be properly synchronized as well. It was my idea.

You don't synchronize flags to prevent concurrent problems. Instead, you synchronize the blocks of codes that might induce concurrent access problems.


In our case, the sensitive fragment is the dependency of isKeyPressed flag (the if condition), followed by a piece of code of which the execution depends on whether isKeyPressed was true or false. If we have another thread altering the state of isKeyPressed BETWEEN those two statements, we get unintended results. So, we synchronize the blocks of codes in a way that it should be accessed by only one thread at a time, which makes sure the isKeyPressed won't be changed during the period we want it to remain intact.

ExamLab © 2008 - 2024