package net.proteanit.demo;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
enum AccessType {
READER,
WRITER;
}
/**
* This is a simple game of chance to demonstrate ReadWrite locks in Java: a
* writer acquires a write lock and rewrites a List by shuffling it.
* If this ends up being in the correct order, the next reading thread to read
* the data wins the game
*
* @author Charles Johnson
* @version 1.0
*/
public class LockGame {
private final ReentrantReadWriteLock rwl;
private final List<String> data;
private final int correctOrder;
private final Lock r;
private final Lock w;
private final Logger log;
private ExecutorService threads;
private volatile boolean playerHasWon;
public LockGame() {
log = Logger.getLogger(LockGame.class);
rwl = new ReentrantReadWriteLock();
data = new ArrayList<String>();
data.add("alpha");
data.add("beta");
data.add("gamma");
correctOrder = data.hashCode();
r = rwl.readLock();
w = rwl.writeLock();
}
private void start() {
final int SIZE = 16;
List<Callable<Void>> workers = new ArrayList<Callable<Void>>(SIZE);
for (int i = 0; i < SIZE; i++) {
workers.add(new ReaderWriter());
}
threads = Executors.newFixedThreadPool(SIZE, new PrettyPrintThreadFactory(99, "thread-"));
try {
if (log.isDebugEnabled()) {
log.debug("Invoking all players...");
}
threads.invokeAll(workers);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (RejectedExecutionException e) {
// Ignore this - some thread has won
} finally {
// Another thread might have already called this
stop();
}
}
private void stop() {
if (!threads.isShutdown()) {
threads.shutdownNow();
}
if (!playerHasWon && log.isDebugEnabled()) {
log.debug("There was no winner on that run");
}
}
private void changeData() {
try {
w.lock();
if (log.isDebugEnabled()) {
log.debug(String.format("Acquired lock in changeData() at %d", System.currentTimeMillis()));
}
Collections.shuffle(data);
if (log.isDebugEnabled()) {
log.debug(String.format("Data changed - now %s", data.toString()));
}
try { Thread.sleep((int)(Math.random() * 1000)); } catch(InterruptedException e) { /* ignore it */ }
} finally {
w.unlock();
if (log.isDebugEnabled()) {
log.debug(String.format("Released lock in changeData() at %d", System.currentTimeMillis()));
}
}
}
private void readData() {
try {
// We can't be sure that all threads can be closed down
// after a single winner emerges so we make the boolean check
if (playerHasWon) {
return;
}
r.lock();
if (log.isDebugEnabled()) {
log.debug(String.format("Acquired lock in readData() at %d", System.currentTimeMillis()));
}
if (log.isDebugEnabled()) {
log.debug(String.format("Reading data - now %s", data.toString()));
}
if (data.hashCode() == correctOrder) {
if (log.isDebugEnabled()) {
log.debug("I'VE WON! Data now correct");
}
playerHasWon = true;
stop();
}
else {
try { Thread.sleep((int)(Math.random() * 1000)); } catch(InterruptedException e) { /* ignore it */ }
}
} finally {
r.unlock();
if (log.isDebugEnabled()) {
log.debug(String.format("Released lock in readData() at %d", System.currentTimeMillis()));
}
}
}
public static void main(String[] args) {
LockGame lg = new LockGame();
lg.changeData();
lg.start();
}
private class ReaderWriter implements Callable<Void> {
private AccessType access;
public ReaderWriter() {
// Roughly 1 in 5 chance of being a writer, else reader
access = (Math.random() > 0.8) ? AccessType.WRITER : AccessType.READER;
}
public Void call() {
if (log.isDebugEnabled()) {
log.debug(String.format("This ReaderWriter is of type %s",
access));
}
switch (access) {
case WRITER:
changeData();
return null;
case READER:
readData();
return null;
default:
return null;
}
}
}
}
The log file produced gives us some interesting information, but first we'll do a little processing on it:
| 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: | 14:43:50,957 [main] Acquired lock in changeData() at 1197902630929 14:43:51,486 [main] Released lock in changeData() at 1197902631485 14:43:51,567 [thread-00] Acquired lock in changeData() at 1197902631566 14:43:51,869 [thread-00] Released lock in changeData() at 1197902631869 14:43:51,872 [thread-01] Acquired lock in readData() at 1197902631871 14:43:51,876 [thread-03] Acquired lock in readData() at 1197902631875 14:43:52,070 [thread-03] Released lock in readData() at 1197902632069 14:43:52,636 [thread-01] Released lock in readData() at 1197902632633 14:43:52,640 [thread-04] Acquired lock in changeData() at 1197902632637 14:43:53,626 [thread-04] Released lock in changeData() at 1197902633621 14:43:53,628 [thread-06] Acquired lock in readData() at 1197902633627 14:43:53,631 [thread-08] Acquired lock in readData() at 1197902633630 14:43:53,749 [thread-06] Released lock in readData() at 1197902633749 14:43:54,322 [thread-08] Released lock in readData() at 1197902634321 14:43:54,323 [thread-07] Acquired lock in changeData() at 1197902634323 14:43:55,310 [thread-07] Released lock in changeData() at 1197902635305 14:43:55,312 [thread-09] Acquired lock in readData() at 1197902635311 14:43:55,315 [thread-02] Acquired lock in readData() at 1197902635314 14:43:55,493 [thread-09] Released lock in readData() at 1197902635493 14:43:55,514 [thread-02] Released lock in readData() at 1197902635509 14:43:55,516 [thread-10] Acquired lock in changeData() at 1197902635516 14:43:55,554 [thread-10] Released lock in changeData() at 1197902635553 14:43:55,556 [thread-05] Acquired lock in readData() at 1197902635555 14:43:55,561 [thread-12] Acquired lock in readData() at 1197902635559 14:43:55,918 [thread-05] Released lock in readData() at 1197902635917 14:43:56,546 [thread-12] Released lock in readData() at 1197902636545 14:43:56,547 [thread-13] Acquired lock in changeData() at 1197902636547 14:43:57,118 [thread-13] Released lock in changeData() at 1197902637117 14:43:57,119 [thread-14] Acquired lock in readData() at 1197902637119 14:43:57,122 [thread-11] Acquired lock in readData() at 1197902637121 14:43:57,124 [thread-15] Acquired lock in readData() at 1197902637123 14:43:57,394 [thread-14] Released lock in readData() at 1197902637393 14:43:57,534 [thread-15] Released lock in readData() at 1197902637533 14:43:58,010 [thread-11] Released lock in readData() at 1197902638009 |
The important thing to note is that in places like lines 9-10 and 21-22 the write thread enters and exits in an orderly fashion with no competition.
In contrast, in place like lines 5-8, concurrent threads co-exist happily when only reading is being done. 'thread-01' gets a read lock in line 5, followed by 'thread-03' before thread-01 has released its lock.
No comments:
Post a Comment