The lock interface in Java has a specialized version - ReadWriteLock. As per the interface documentation:
A ReadWriteLock maintains a pair of associated locks, one for read-only operations and one for writing. The read lock may be held simultaneously by multiple reader threads, so long as there are no writers.The write lock is exclusive. All ReadWriteLock implementations must guarantee that the memory synchronization effects of writeLock operations (as specified in the Lock interface) also hold with respect to the associated readLock. That is, a thread successfully acquiring the read lock will see all updates made upon previous release of the write lockTo test the same class I created a StockTracker class that keeps track of the Stock values.
class StockTracker { private final Map<String, Float> tickers = new HashMap<String, Float>(); private final ReadWriteLock tickerLock = new ReentrantReadWriteLock(); private final Lock readLock = tickerLock.readLock(); private final Lock writeLock = tickerLock.writeLock(); public Float getValue(String caller, String key) { Float val = null; System.out.println(caller + " trying to read value for " + key + " at timestamp " + System.currentTimeMillis() + "..."); readLock.lock(); val = tickers.get(key); System.out.println(caller + " read value for " + key + " at timestamp " + System.currentTimeMillis()); readLock.unlock(); return val; } public void addValue(String caller, String key, Float value) { System.out.println(caller + " trying to write value for " + key + " at timestamp " + System.currentTimeMillis() + "..."); writeLock.lock(); tickers.put(key, value); System.out.println(caller + " wrote value for " + key + " at timestamp " + System.currentTimeMillis()); writeLock.unlock(); } }As seen there are two methods - the getValue() used by readers and the addValue() used by writers. The lock itself is composed of two locks:
private final ReadWriteLock tickerLock = new ReentrantReadWriteLock(); private final Lock readLock = tickerLock.readLock(); private final Lock writeLock = tickerLock.writeLock();For the read operation we accordingly used the readLock and for write we used the writeLock. my writer class is as below:
class Agent implements Runnable { private String stockName; private StockTracker stockTracker; public Agent(String stockName, StockTracker stockTracker) { this.stockName = stockName; this.stockTracker = stockTracker; } public void run() { for (int i = 0; i < 2; i++) { stockTracker.getValue(Thread.currentThread().getName(), stockName); } } }similarly the reader is as below:
class StockMarketEmployee implements Runnable { private String stockName; private StockTracker stockTracker; public StockMarketEmployee(String stockName, StockTracker stockTracker) { this.stockName = stockName; this.stockTracker = stockTracker; } public void run() { for (int i = 0; i < 2; i++) { stockTracker.addValue(Thread.currentThread().getName(), stockName, (float) (Math.random() * 100)); } } }I decided to run the code:
public static void main(String[] args) { StockTracker stockTracker = new StockTracker(); new Thread(new StockMarketEmployee("IBM", stockTracker), "Emp 1").start(); new Thread(new Agent("IBM", stockTracker), "Buyer 1").start(); new Thread(new Agent("GOOG", stockTracker), "Seller 2").start(); }The output is:
Buyer 1 trying to read value for IBM at timestamp 1383914697045... Emp 1 trying to write value for IBM at timestamp 1383914697045... Buyer 1 read value for IBM at timestamp 1383914697046 Seller 2 trying to read value for GOOG at timestamp 1383914697045... Emp 1 wrote value for IBM at timestamp 1383914697056 Emp 1 trying to write value for IBM at timestamp 1383914697056... Emp 1 wrote value for IBM at timestamp 1383914697056 Seller 2 read value for GOOG at timestamp 1383914697056 Buyer 1 trying to read value for IBM at timestamp 1383914697067... Buyer 1 read value for IBM at timestamp 1383914697067 Seller 2 trying to read value for GOOG at timestamp 1383914697078... Seller 2 read value for GOOG at timestamp 1383914697078It would be futile to try and predict anything from the above results. From the java docs:
A read-write lock allows for a greater level of concurrency in accessing shared data than that permitted by a mutual exclusion lock. It exploits the fact that while only a single thread at a time (a writer thread) can modify the shared data, in many cases any number of threads can concurrently read the data (hence reader threads). Whether or not a read-write lock will improve performance over the use of a mutual exclusion lock depends on the frequency that the data is read compared to being modified, the duration of the read and write operations, and the contention for the data - that is, the number of threads that will try to read or write the data at the same time. For example, a collection that is initially populated with data and thereafter infrequently modified, while being frequently searched (such as a directory of some kind) is an ideal candidate for the use of a read-write lock. However, if updates become frequent then the data spends most of its time being exclusively locked and there is little, if any increase in concurrency.
No comments:
Post a Comment