Java implements monitors,
but with a single queue. The queue is “anonymous” (but is really implicitly
named by the object in which it is defined, as we shall see).
Any class definition can be augmented to
include monitor like behaviour. We add a qualifier synchronized
to any method that requires exclusive access to the instance variables of
the class. Thus, if we have two or more functions that are declared to be synchronized and one
of them is currently being executed, any attempt to enter another synchronized method
will be blocked and force the thread into the external queue. In Java
terminology, there is a lock associated with the object. Only one thread can
own the lock at one time and only the thread that owns the lock can execute a
synchronized method. At the end of the synchronized method, the thread
relinquishes the lock.
Inside a synchronized method, the wait() statement
behaves like the normal wait() statement in a monitor, except
that there is only one (unnamed) queue that the process can wait on. The
corresponding wakeup call should be called notify() but in
Java, for some obscure reason, there are two functions notify()
and notifyAll(). The function notify() signals
only one of the waiting processes, at random. Instead, notifyAll()
signals all waiting processes (as one would expect) and this should be
used by default.
Java monitors use the signal
and continue mechanism, so the notifying process can continue execution after sending
a wakeup signal and the processes that are woken up move to the external queue
and are in contention to grab the object lock when it next becomes available.
The Java implementation of the bank
account class looks pretty much like the monitor description given earlier. One
difference is that it can include non-synchronized methods, which do not
require having control of the object lock.
public class bank_account{
double accounts[100]; // transfer "amount" from
accounts[source] to accounts[target]
public synchronized
boolean
transfer (double amount,
int source, int target){
while (accounts[source]
< amount){
wait();
}
accounts[source] -=
amount;
accounts[target] +=
amount;
notifyAll();
return true;
}
// compute
the total balance across all accounts
public synchronized
double audit(){
double balance = 0.0;
for (int i = 0; i <
100; i++){
balance += accounts[i];
}
return balance;
}
// a non-synchronized
method
public double
current_balance(int i){
return accounts[i];
}
}
In addition to synchronized methods,
Java has a slightly more flexible mechanism. Every object has a lock, so an
arbitrary block of code can be synchronized with respect to any object, as
follows:
public class XYZ{
Object o = new Object();
public int f(){
..
synchronized(o){
...
}
}
public double g(){
..
synchronized(o){
...
}
}
Now, f() and g() can both
begin to execute in parallel in different threads, but only one thread at a
time can grab the lock associated with o and enter
the block enclosed by synchronized(o). The other will be placed in the
equivalent of an “external queue” for the object o, while
waiting for the object lock.
In addition, there is a separate “internal
queue” associated with o, so one can write.
public class XYZ{
Object o = new Object();
public int f(){
..
synchronized(o){
...
o.wait(); // Wait in
the internal queue attached to "o"
...
}
}
public double g(){
..
synchronized(o){
...
o.notifyAll(); // Wake up the internal queue attached to
"o"
...
}
}
Notice that we can “rewrite” completely
a synchronized method of the form
public synchronized
double h(){
...
}
as an externally
unsynchronized method that is internally synchronized on this as
follows:
public double h(){
synchronized(this){
...
}
}
Also, the “anonymous” calls wait() and notify()/notifyAll()
are actually the normal object-oriented abbreviations for this.wait()
and this.notify()/this.notifyAll().
Actually, wait() throws an
exception InterruptedException (that we shall examine when we look at how to
define threads in Java), so more correctly we should encapsulate calls to wait() (or o.wait()) as
follows:
try{
wait();
}
catch
(InterruptedException e);
Also, it is a mistake to use wait() or notify()/notifyAll()
other than in a synchronized method. This will throw an IllegalMonitorStateException.
Similarly, it is an error to to use o.wait(), o.notify()
or o.notifyAll() other than within a block synchronized on o.
- Java threads and Interrupts in Java programming language
- Abstract classes and Generic function in Java
- Callback functions in Java Programming language
- Constructors in programming language
- Static components and constants concept in programming language
- Data encapsulation in programming language.
- Java threads and Interrupts in Java programming language
Post a Comment