|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 7
ReentrantLock behaves like synchronized and you might wonder when it's appropriate to use one or the other. Use ReentrantLock when you need timed or interruptible lock waits, non-block-structured locks (obtain a lock in one method; return the lock
in another), multiple condition variables, or lock polling. Furthermore, ReentrantLock supports scalability and is useful where there is high contention among threads. If none of these factors come into play,
use synchronized.
ReentrantLock declares the following constructors:
ReentrantLock() creates a reentrant lock.
ReentrantLock(boolean fair) creates a reentrant lock with the given fairness policy. Passing true to fair results in a lock that uses a fair ordering policy, which means that under contention, the lock favors granting access to the longest-waiting thread. The former constructor
invokes this constructor, passing false to fair.
ReentrantLock implements Lock's methods: its implementation of unlock() throws java.lang.IllegalMonitorStateException when the calling thread doesn't hold the lock. Additionally, ReentrantLock provides its own methods, including the following trio:
int getHoldCount() returns the number of holds on this lock by the current thread: a thread has a hold on a lock for each lock action that isn't matched by an unlock action.
When the lock() method is called and the current thread already holds the lock, the hold count is incremented by one and the method returns
immediately.
boolean isFair() returns the fairness setting.
boolean isHeldByCurrentThread() queries if this lock is held by the current thread, returning true when this is the case. This method is often used for debugging
and testing.
Listing 1 is a simple demonstration of ReentrantLock.
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo
{
public static void main(String[] args)
{
ExecutorService executor = Executors.newFixedThreadPool(2);
final ReentrantLock rl = new ReentrantLock();
class Worker implements Runnable
{
private String name;
Worker(String name)
{
this.name = name;
}
@Override
public void run()
{
rl.lock();
try
{
if (rl.isHeldByCurrentThread())
System.out.printf("Thread %s has entered its critical section.%n",
name);
System.out.printf("Thread %s is performing work for 2 seconds.%n", name);
try
{
Thread.sleep(2000);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
System.out.printf("Thread %s has finished working.%n", name);
}
finally
{
rl.unlock();
}
}
}
executor.execute(new Worker("A"));
executor.execute(new Worker("B"));
try
{
executor.awaitTermination(5, TimeUnit.SECONDS);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
executor.shutdownNow();
}
}
Listing 1 creates two worker threads. Each thread first acquires a lock to ensure that it has complete access to the critical section. It then outputs some messages and sleeps for two seconds to simulate work. After outputting another message, it releases the lock.
Previous articles in Java 101: The next generation
java.util.concurrent, with Jeff Friesen's detailed introduction to the Executor framework, synchronizer types, and the Java Concurrent Collections
package.
Concurrency tutorials on JavaWorld:
java.util.concurrent to work around deadlock and similar threading pitfalls.
CountDownLatch and Executors.newFixedThreadPool concurrency utilities.
java.util.concurrent classes were used to optimize thread use for faster performance in a real-world application.
ExecutorService class with ForkJoinPool in a web crawler.