|
|
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 6 of 7
The ReadWriteLock interface maintains a pair of associated locks, one for read-only operations and one for write operations. The read lock may be held simultaneously by multiple reader threads as long as there are no writers. The write lock is exclusive: only a single thread can modify shared data. (The lock that's associated with the synchronized keyword is also exclusive.)
ReadWriteLock declares the following methods:
Lock readLock() returns the lock that's used for reading.
Lock writeLock() returns the lock that's used for writing.
The ReentrantReadWriteLock class implements ReadWriteLock and describes a read-write lock with similar semantics to a reentrant lock. Like ReentrantLock, ReentrantReadWriteLock declares a pair of constructors:
ReentrantReadWriteLock() creates a reentrant read-write lock with default (nonfair) ordering properties.
ReentrantReadWriteLock(boolean fair) creates a reentrant read-write lock with the given fairness policy.
ReentrantReadWriteLock implements ReadWriteLock's methods and provides additional methods, including the following trio:
int getQueueLength() returns an estimate of the number of threads waiting to acquire either the read or write lock.
int getReadHoldCount() returns the number of read holds on this lock by the current thread. A reader thread has a hold on a lock for each lock action
that is not matched by an unlock action.
boolean hasWaiters(Condition condition) returns true when there are threads waiting on the given condition associated with the write lock.
Listing 3 demonstrates ReentrantReadWriteLock.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RWLockDemo
{
final static int DELAY = 80;
final static int NUMITER = 5;
public static void main(String[] args)
{
final Names names = new Names();
class NamedThread implements ThreadFactory
{
private String name;
NamedThread(String name)
{
this.name = name;
}
@Override
public Thread newThread(Runnable r)
{
return new Thread(r, name);
}
}
ExecutorService writer;
writer = Executors.newSingleThreadExecutor(new NamedThread("writer"));
Runnable wrunnable = new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < NUMITER; i++)
{
names.add(Thread.currentThread().getName(),
"A" + i);
try
{
Thread.sleep(DELAY);
}
catch (InterruptedException ie)
{
}
}
}
};
writer.submit(wrunnable);
ExecutorService reader1;
reader1 = Executors.newSingleThreadExecutor(new NamedThread("reader1"));
ExecutorService reader2;
reader2 = Executors.newSingleThreadExecutor(new NamedThread("reader2"));
Runnable rrunnable = new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < NUMITER; i++)
names.dump(Thread.currentThread().getName());
}
};
reader1.submit(rrunnable);
reader2.submit(rrunnable);
reader1.shutdown();
reader2.shutdown();
writer.shutdown();
}
}
class Names
{
private final List<String> names;
private final ReentrantReadWriteLock lock;
private final Lock readLock, writeLock;
Names()
{
names = new ArrayList<>();
lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
}
void add(String threadName, String name)
{
writeLock.lock();
try
{
System.out.printf("%s: num waiting threads = %d%n",
threadName, lock.getQueueLength());
names.add(name);
}
finally
{
writeLock.unlock();
}
}
void dump(String threadName)
{
readLock.lock();
try
{
System.out.printf("%s: num waiting threads = %d%n",
threadName, lock.getQueueLength());
Iterator<String> iter = names.iterator();
while (iter.hasNext())
{
System.out.printf("%s: %s%n", threadName, iter.next());
try
{
Thread.sleep((int)(Math.random()*100));
}
catch (InterruptedException ie)
{
}
}
}
finally
{
readLock.unlock();
}
}
}
Listing 3 describes an application where a writer thread appends names to a list of names and a pair of reader threads repeatedly dump this list to the standard output.
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.