Threads and Multithreading – Part 5

Threads - images of multicoloured threads

This is part 5 of this series. If you missed any of the last four, you can find them here:

In this post, we’ll look at thread synchronization and the monitor model.

Thread Synchronization

In most practical multithreaded applications, two or more threads may need to share access to the same objects. If two threads have access to the same object, and each calls a method that modifies the state of the object, the data stored in the object may become corrupted. This is commonly known as a race condition. It is therefore important to ensure exclusive access to methods that update or modify data. For example, if the deposit() and withdraw() methods on a BankAccount object are called simultaneously, the balance may not be correctly calculated.

To avoid simultaneous access of a shared object by multiple threads, access must be synchronized. A synchronized method can only be executed by one thread at a time. While one thread is executing a synchronized method, any other thread that wants to execute either the same method or any other synchronized method on the same object will be forced to wait until the first thread has finished executing the synchronized block.

Object Locks

Every Java object has an invisible one-bit field called a monitor or object lock. This monitor controls access to the object: it is set (becomes unavailable) when a synchronized method is executing. Once it is set, other synchronized methods of the object must wait until the bit has been reset (becomes available) before they can execute.

A thread will release the object lock when it has finished executing the synchronized method, or when it exits the method by throwing an exception.

If a thread owns the lock of an object and it calls another synchronized method of the same object, then that thread is automatically granted access to the method. The thread only relinquishes the lock when it exits the last synchronized method.

It is important to understand that it is the object that is locked, and not the method itself. Multiple threads can access different instances of the same class without synchronization issues. They can even invoke the same synchronized method at the same time, provided the method is invoked on different objects.

The class itself also has a lock: this is used for synchronized static methods.

The synchronized Keyword

A method is synchronized by using the synchronized keyword. For example:

public class BankAccount {

    // a primitive data type here
    private double balance;

    public synchronized void deposit(double amount) {
        balance += amount;
    }
} // end of class

The synchronized keyword can be applied to a method as we’ve just seen, or to a code block. Applying it to a code block allows finer granularity of control and faster processing because fewer statements are locked. Deciding which statements can be outside the block requires a lot of careful thought, though.

public void deposit(double amount) {
    statements;                 // not synchronized
    synchronized (this) {
        balance += amount;      // synchronized statements
    }
    statements;                 // not synchronized
}

The this keyword in this context specifies that we are synchronizing access on the current object’s monitor. We can also use the monitor of any other object (most probably an instance field of the first object):

public class BankAccount {

   // an object reference now instead of a primitive type
    private Double balance;

    public void deposit(Double amount) {
        statements;                 // not synchronized
        synchronized (balance) {
            balance += amount;      // synchronized statements
        }
        statements;                 // not synchronized
    }
} // end of class

Other threads are free to call any unsynchronized methods on a locked object. For example, we could have a getAccountNumber() method in the BankAccount class that could access the account number. This method would not need to be synchronized because it would not change the balance field. So getAccountNumber() could therefore be run even if another thread was running the synchronized deposit() method.

Synchronized Methods

There are several other methods that we use when working with an object’s monitor. These methods are in the Object class:

  • Once a thread has entered a synchronized method or code block, it may not be able to proceed for some reason. The thread can then call the wait() method and enter a wait list for that object. The scheduler ignores the thread until it’s removed from the wait list. This is done when the thread is notified of a change in the object’s status.

  • The wait() method can take a long argument which specifies the maximum length of time that the thread will wait. If wait() is called without a parameter, the thread will wait indefinitely.

  • When a thread calls wait() within a synchronized method, it releases the lock. This allows another thread to enter a synchronized code block on the same object. This thread might reset the condition that caused the first thread to enter the wait state. Once the condition is corrected, this subsequent thread should call the notify() or notifyAll() method before leaving the synchronized block.

  • To remove a thread from the wait list, some other thread must call the notify() or notifyAll() method on the same object.

    • The notify() method removes a single arbitrarily selected thread from the wait list.
    • The notifyAll() method will remove all threads from that object’s wait list.

Consider the following simplified example using wait() and notify():

public class BankAccount {

    // a primitive data type here
    private double balance;

    public synchronized double withdraw (double amount) {
        while (balance < amount) {
            wait();
        }
        balance -= amount;
        return amount;
    }

    public synchronized void deposit(double amount) {
        balance += amount;
        notifyAll();
    }
} // end of class

Obviously this example is contrived because we definitely wouldn’t want a withdrawal from a bank account to block indefinitely while we waited for a deposit.

The Java API documentation contains a lot more information on how to use the wait(), notify() and notifyAll() methods.

Conclusion

In the next post we’ll start looking at the new concurrency classes in the java.util.concurrent package.

I look forward to reading your comments.

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.