Multithreading - Locks

Traditional multithreaded programming comes with a single way to get the lock i.e. synchronized keyword. All the threads wait until a thread releases the lock i.e. leaves the synchronized block.

With the new concurrent api, locks were introduced to give the following facilities :
1. You can try for acquiring a lock, if you get acquire it, if not, you do something else/wait as you wish.
2. You can specify for how much time you want to wait to acquire a lock. This is one of the ways to avoid deadlocks, since thread will not go in a wait state forever.
3. If multiple threads are waiting for a lock, we now have an option to define if the thread waiting for the longest time gets the lock.
4. synchronized keyword was applicable only at method level or block level, but now we have an option to use locks across multiple methods.

The locks functionality is present in the java.util.concurrent.locks package starting JDK 1.5

The top interface here is Lock, which defines the basic mechanism used to acquire and release lock of an object.

The methods in the Lock interface are :

void lock() - acquire the lock else wait until it gets the lock. Same as synchronized.
boolean tryLock() - try to acquire the lock and gets if its available. returns false if unable to get.
boolean tryLock(long time, TimeUnit tu) - same as above but try to acquire lock for specified time.
void lockInterruptibly() - gets the lock if available else wait. if the thread dies, it throws                                                                InterruptedException.
void unlock() - release the lock else throws IllegalMonitorStateException
Condition newCondition() - returns a Condition object associated with the invoking lock.

The implementation class for the Lock interface is the ReentrantLock. As the name suggests, a thread that has acquired the lock can re-enter the lock i.e. it can increase the hold count on the lock. Now we need to keep in mind, that the times a thread calls lock(), it has to call unlock() the same number of times.

ReentrantLock comes with the following constructors :

ReentrantLock() - creates a reentrantlock object
ReentrantLock(boolean fairness) - creates a reentrantlock, fairness means longest waiting thread gets                                                         the lock

Consider the printer example that we had used in synchronized keyword example.

What we will do is we will add lock functionality to the nonsynchronizedPrinter() method and see how we can make it work similar to synchronized keyword method without using synchronized.

public void nonSynchronizedPrinter(String name) {
    lock.lock(); //comment this for synchronized keyword example    for(int i=0;i<250;i++) {
        System.out.println(name + " " + i);    }
    System.out.println("Number of threads waiting : " + lock.getQueueLength());    lock.unlock(); //comment this for synchronized example}

If you see the output of the example, you will see it works same as using the synchronized keyword but here you have the option to release the lock in any method you like. This was a simple example.

The example for reentrant locks can be found here

java.util.concurrent.locks also defines the ReadWriteLock interface. This interface specifies a lock that maintains separate locks for read and write access. This enables multiple locks to be granted for readers of a resource as long as the resource is not being written. ReentrantReadWriteLock provides an implementation of ReadWriteLock.



Comments

Popular posts from this blog

Collection Framework - HashSet And LinkedHashSet class

Collection Framework - Cursors in Java

Hashed data structures