[ Previous: 3. Advantages and Disadvantages | Up: Site Map | Next: 5. Conclusion ]

4. Alternatives

Monitors

Well first and foremost it’s a language based mechanism, where in semaphores are low-level synchronization primitives. Monitors were also conceived to address the short comings of semaphores, in chief, the scattered nature of semaphores and the low level implementation, i.e. the convenience of forgetting P or a V, and to address the former reason in a semaphore based implementation, if you want to make changes, it is absolutely compulsory to find all places in the code, and amend accordingly. Which might take a very long time, and could be error prone for obvious reasons.

While semaphores is a special type of counter, monitors are a shared object. A monitor might have many entry points (condition variable), however, only one process can used to enter the monitor at any time. Hence, it implements mutual exclusion in contrast to semaphores (if init n > 1). A monitor consists of:

Suppose a procedure ought to be implemented if a certain condition is met, i.e a process can suspend itself and release the monitor, this is achieved with condition variable. Another process wanting to access that function/procedure would be queued for that particular procedure.



(http://codex.cs.yale.edu/avi/os-book/os7/slide-dir/ch6.ppt)

Continuing on with our favourite Hollywood actress Angelina Jolie, we would show how, her bank account could be implemented.

monitor Jolieaccount {
    int balance := 0

    function withdraw(int amount) {
        if amount < 0 then error "Amount may not be negative"
        else if balance < amount then error "Insufficient funds"
        else balance := balance - amount
    }

    function deposit(int amount) {
        if amount < 0 then error "Amount may not be negative"
        else balance := balance + amount
    }
}

Notice that in the functions, P and V aren’t explicitly stated, this is because the compiler inserts it in.

In the code above the invariant balance, specifies that the balance, reflect past transactions. Since the monitor has a lock associated only Jolie or Pitt could access the account in a given time. So no worries to their millions.

Suppose Jolie decided for every 1000$ she deposited, she wanted a $100 to be given to a charity, all that has to be done is to come and insert the relevant code in the function deposit. However, in the semaphore based implementation, each and every place where the critical section is, we would have to add it. So as you can see it would be very cumbersome.

Lets look at the problem with the implementation of condition variables.

class Account extends Object {
    private double balance = 0;
    private CondVar OKtoWithdraw = new CondVar;

    public synchronized void deposit(double amount) {
        balance = balance + amount;
        notifyAll(OKtoWithdraw);
    }

    public synchronized void withdraw(double amount) {
        while (amount > balance)
            wait(OKtoWithdraw);
        balance = balance - amount;
    }
}

wait(CondVar cond) {
    put the calling thread on the “wait set” of cond;
    release lock;
    Thread.currentThread.suspend();
    acquire lock;
}

notifyAll(CondVar cond) {
    forall t in wait set of cond
    t.resume()
}

There is one condition variables declared here, OktoWithdraw. In this monitor, the deposit method/function/procedure after updating the balance, calls notifyAll(OktoWithdraw), in which, it would notify all threads in queue and they would resume activity. The rationale behind this is we may have some thread not able to withdraw the money because the amount is greater than the balance. After a deposit, we would want that thread to resume activity and check whether the balance now contains enough money for withdrawal.

When withdrawing, the procedure checks if the amount requested for withdrawal is greater than the balance and then calls the wait(OKtoWithdraw). In the wait method, the calling thread is enqueued in the queue associated with the condition variable OktoWithdraw. The monitor lock is released and the calling thread suspends operation. The other threads which were waiting earlier for the monitor lock now try to acquire the lock again. If the thread requests more money than the balance, it is made to wait (again) and the lock is passed on to the next thread trying to access it. When the condition finally becomes true, i.e. the amount of withdrawal is less than the balance (maybe after a deposit), the line "balance = balance - amount;" is carried out to update the balance.

Java Synchronization (Monitors)

Java uses the “synchronized “keyword to block a statement or a chunk of code so that only one thread can access it at a time. The “synchronized” keyword acts like a lock and it blocks all other threads from entering into that section of code, when it is being used by a thread. This is the Java way of implementing monitors.

In addition to placing the “synchronized” keyword before a chunk of code, it may also be used in conjunction with “wait”,”notify” or “notifyAll” statements, as these allow threads to wait when another thread wants to take over the “synchronized” code.

Let’s understand better with the following code:

Hope you all remember the scenario where Angelina Jolie and Brad Pitt tried accessing Jolie’s account at the same time and was left with $0! Well so the banks nowadays prevented that from happening by using a similar code like:

/**
 * Make a deposit to this account
 * @param depositAmount amount to deposit in pennies
 */

public synchronized void deposit( long depositAmount ) throws BankingException
{
    if ( depositAmount < 0 )
    {
        throw new BankingException( "Negative deposits should be handled
        as corrections or withdrawals" );
    }
    this.balance += depositAmount;
}

/**
 * Make a withdrawal from this account
 * @param withDrawAmount amount to withdraw in pennies
 */

public synchronized void withdraw( long withdrawAmount ) throws BankingException
{
    if ( withdrawAmount < 0 )
    {
        throw new BankingException( "Negative withrawals should be handled as
        corrections or deposits" );
    }
    if ( balance < withdrawAmount )
    {
        throw new BankingException( "Insufficient funds" );
    }
    this.balance -= withDrawAmount;
}

The situation now becomes as we would prefer:

Angelina Jolie

Brad Pitt

Load currentBalance=1 million
Deposit newAmount=1 million
(new balance 2 million)
Update currentBalance=2 million
 
  Load currentBalance=2 million
Withdraw newAmount=1 million
(new balance 1 million)
Update currentBalance=1 million
References
1. Multithreaded Programming with ThreadMentor: A Tutorial
2. Semaphores & Monitors
3. Monitor (Wikipedia)
4. Semaphore vs Monitor
5. Empirical Evaluation of a UML Sequence Diagram with Adornments to Support Understanding of Thread Interactions

[ Previous: 3. Advantages and Disadvantages | Up: Site Map | Next: 5. Conclusion ]