Thankfully, lock utility classes were added in Java 1.5 and make these problems easier to solve.
Java Reentrant Locks
Java has a few lock implementations in the java.util.concurrent.locks package.
The general classes of locks are nicely laid out as interfaces:
- Lock - the simplest case of a lock which can be acquired and released
- ReadWriteLock - a lock implementation that has both read and write lock types – multiple read locks can be held at a time unless the exclusive write lock is held
- ReentrantLock - as you’d expect, a reentrant Lock implementation
- ReentrantReadWriteLock - a reentrant ReadWriteLock implementation
Extended capabilities with Reentrant locks
The ReentrantLock in the util.concurrent.locks package gives the developers some flexibility here. With ReentrantLock following are some of the options
- tryLock() : With ReentrantLock, the thread can immediately return if it did not get the lock (if the lock is with some other thread).
tryLock(long timeout, TimeUnit unit)
:With ReentrantLock, the thread can wait for some duration to get hold of the lock. If it does not get the lock within some time, it will return.- lockInterruptibly() : With ReentrantLock, the thread waiting for the lock can be interrupted and cause it to come out with InterruptedException.
final ReentrantLock _lock = new ReentrantLock();
private void method() throws InterruptedException
{
//Trying to enter the critical section
_lock.lock(); // will wait until this thread gets the lock
try
{
// critical section
}
finally
{
//releasing the lock so that other threads can get notifies
_lock.unlock();
}
}
Using optional “fairness” parameter with ReentrantLock
ReentrantLock accepts an optional “fairness” parameter in it’s constructor. Normally what happens is, whenever a thread releases the lock anyone of the waiting threads will get the chance to acquire that lock. But there is no predefined order or priority in the selection of the thread (at least from a programmers perspective).
But if we are specifying the fairness parameter as “true” while creating a new ReentrantLock object, it gives us the guaranty that the longest waiting thread will get the lock next. Sounds pretty nice right?
Use of “Condition” in ReentrantLock
Condition can be considered as a separation of monitor methods (wait(), notify() & notifyAll()). For each ReentrantLock we can define a set of conditions and based on that we can make the threads waiting & things like that.
import java.util.concurrent.locks.Condition;
final Condition _aCondition = _lock.newCondition();
private void method1() throws InterruptedException
{
_lock.lock();
try
{
while (condition 1)
{
// Waiting for the condition to be satisfied
// Note: At this time, the thread will give up the lock
// until the condition is satisfied. (Signaled by other threads)
_aCondition.await();
}
// method body
}
finally
{
_lock.unlock();
}
}
private void method2() throws InterruptedException
{
_lock.lock();
try
{
doSomething();
if (condition 2)
{
// Signaling other threads that the condition is satisfied
// Wakes up any one of the waiting threads
_aCondition.signal();
// Wakes up all threads waiting for this condition
_aCondition.signalAll();
}
// method body
}
finally
{
_lock.unlock();
}
}
Example
Counter.java
package com.vaani.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count;
private Lock lock = new ReentrantLock();
public int getNextValue() {
try {
lock.lock();
count++;
}
finally {
lock.unlock();
return count;
}
}
}
Now worker threads starts on the counter object and start incrementing the value:
package com.vaani.lock;
public class Worker implements Runnable {
private Counter counter;
private boolean increment;
private int count;
public Worker(Counter counter, boolean increment, int count) {
this.counter = counter;
this.increment = increment;
this.count = count;
}
public void run() {
for (int i = 0; i < this.count; i++) {
System.out.println(this.counter.getNextValue());
}
}
}
package com.vaani.lock.demo;
import com.vaani.lock.*;
public class ReentrantLockDemo {
public static void main(String[] args) throws Exception {
Counter counter = new Counter();
Thread t1 = new Thread(new Worker(counter, true, 10000));
t1.start();
Thread t2 = new Thread(new Worker(counter, false, 10000));
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + counter.getNextValue());
}
}
Download the source
Source code above can be downloaded from here.
No comments:
Post a Comment