这个例子是将一个模拟的内存文件由 producer 读到buffer中, 然后由consumer从buffer中读取的例子,通过Condition来实现协调。
buffer:文件的缓存
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* implement a class named Buffer that will implement the buffer shared by
* producers and consumers.
*
* @author bird 2014年9月21日 下午6:58:13
*/
public class Buffer {
private LinkedList<String> buffer;
private int maxSize;
private ReentrantLock lock;
private Condition lines;
private Condition space;
private boolean pendingLines;
/**
* Implement the constructor of the class. It initializes all the attributes
* described previously.
*
* @param maxSize
*/
public Buffer(int maxSize) {
this.maxSize = maxSize;
buffer = new LinkedList<String>();
lock = new ReentrantLock();
lines = lock.newCondition();
space = lock.newCondition();
pendingLines = true;
}
/**
* Implement the insert() method. It receives String as a parameter and
* tries to store it in the buffer. First, it gets the control of the lock.
* When it has it, it then checks if there is empty space in the buffer. If
* the buffer is full, it calls the await() method in the space condition to
* wait for free space. The thread will be woken up when another thread
* calls the signal() or signalAll() method in the space Condition. When
* that happens, the thread stores the line in the buffer and calls the
* signallAll() method over the lines condition. As we'll see in a moment,
* this condition will wake up all the threads that were waiting for lines
* in the buffer.
*
* @param line
*/
public void insert(String line) {
lock.lock();
try {
while (buffer.size() == maxSize) {
space.await();
}
buffer.add(line);
System.out.printf("%s: Inserted Line: %d\n", Thread.currentThread()
.getName(), buffer.size());
lines.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* Implement the get() method. It returns the first string stored in the
* buffer. First, it gets the control of the lock. When it has it, it checks
* if there are lines in the buffer. If the buffer is empty, it calls the
* await() method in the lines condition to wait for lines in the buffer.
* This thread will be woken up when another thread calls the signal() or
* signalAll() method in the lines condition. When it happens, the method
* gets the first line in the buffer, calls the signalAll() method over the
* space condition and returns String.
*
* @return
*/
public String get() {
String line = null;
lock.lock();
try {
while ((buffer.size() == 0) && (hasPendingLines())) {
lines.await();
}
if (hasPendingLines()) {
line = buffer.poll();
System.out.printf("%s: Line Readed: %d\n", Thread
.currentThread().getName(), buffer.size());
space.signalAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return line;
}
/**
* Implement the setPendingLines() method that establishes the value of the
* attribute pendingLines. It will be called by the producer when it has no
* more lines to produce.
*
* @param pendingLines
*/
public void setPendingLines(boolean pendingLines) {
this.pendingLines = pendingLines;
}
/**
* Implement the hasPendingLines() method. It returns true if there are more
* lines to be processed, or false otherwise.
*
* @return
*/
public boolean hasPendingLines() {
return pendingLines || buffer.size() > 0;
}
}
FileMock:需要读取的文件
/**
* First, let's implement a class that will simulate a text file. Create a class
* named FileMock with two attributes: a String array named content and int
* named index. They will store the content of the file and the line of the
* simulated file that will be retrieved.
*
* @author bird 2014年9月21日 下午6:55:31
*/
public class FileMock {
private String content[];
private int index;
/**
* Implement the constructor of the class that initializes the content of
* the file with random characters.
*
* @param size
* @param length
*/
public FileMock(int size, int length) {
content = new String[size];
for (int i = 0; i < size; i++) {
StringBuilder buffer = new StringBuilder(length);
for (int j = 0; j < length; j++) {
int indice = (int) (Math.random() * 255);
buffer.append((char) indice);
}
content[i] = buffer.toString();
}
index = 0;
}
/**
* Implement the method hasMoreLines() that returns true if the file has
* more lines to process or false if we have achieved the end of the
* simulated file.
*
* @return
*/
public boolean hasMoreLines() {
return index < content.length;
}
/**
* Implement the method getLine() that returns the line determined by the
* index attribute and increases its value.
*
* @return
*/
public String getLine() {
if (this.hasMoreLines()) {
System.out.println("Mock: " + (content.length - index));
return content[index++];
}
return null;
}
}
producer:生产者
public class Producer implements Runnable {
private FileMock mock;
private Buffer buffer;
public Producer(FileMock mock, Buffer buffer) {
this.mock = mock;
this.buffer = buffer;
}
/**
* Implement the run() method that reads all the lines created in the
* FileMock object and uses the insert() method to store them in the buffer.
* Once it finishes, use the setPendingLines() method to alert the buffer
* that it's not going to generate more lines.
*/
@Override
public void run() {
buffer.setPendingLines(true);
while (mock.hasMoreLines()) {
String line = mock.getLine();
buffer.insert(line);
}
buffer.setPendingLines(false);
}
}
Consumer:消费者
public class Producer implements Runnable {
private FileMock mock;
private Buffer buffer;
public Producer(FileMock mock, Buffer buffer) {
this.mock = mock;
this.buffer = buffer;
}
/**
* Implement the run() method that reads all the lines created in the
* FileMock object and uses the insert() method to store them in the buffer.
* Once it finishes, use the setPendingLines() method to alert the buffer
* that it's not going to generate more lines.
*/
@Override
public void run() {
buffer.setPendingLines(true);
while (mock.hasMoreLines()) {
String line = mock.getLine();
buffer.insert(line);
}
buffer.setPendingLines(false);
}
}