一个锁可能关联了一个或多个条件。这些条件可以在Condition接口中声名。
使用这些条件的目的是去控制一个锁并且可以检查一个条件是true或false,如果为false,则暂停直到
另一个线程来唤醒它。
Condition接口提供了这样一个机制 去暂定一个线程和唤醒一个暂停中的线程。
本例中继续拿生产者-消费者问题举例。
FileMock.java
package com.dylan.thread.ch2.c07.utils;
/**
* This class simulates a text file. It creates a defined number
* of random lines to process them sequentially.
*
*/
public class FileMock {
/**
* Content of the simulate file
*/
private String content[];
/**
* Number of the line we are processing
*/
private int index;
/**
* Constructor of the class. Generate the random data of the file
* @param size: Number of lines in the simulate file
* @param length: Length of the lines
*/
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;
}
/**
* Returns true if the file has more lines to process or false if not
* @return true if the file has more lines to process or false if not
*/
public boolean hasMoreLines(){
return index<content.length;
}
/**
* Returns the next line of the simulate file or null if there aren't more lines
* @return
*/
public String getLine(){
if (this.hasMoreLines()) {
System.out.println("Mock: "+(content.length-index));
return content[index++];
}
return null;
}
}
Buffer.java
package com.dylan.thread.ch2.c07.task;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* This class implements a buffer to stores the simulate file lines between the
* producer and the consumers
*
*/
public class Buffer {
/**
* The buffer
*/
private LinkedList<String> buffer;
/**
* Size of the buffer
*/
private int maxSize;
/**
* Lock to control the access to the buffer
*/
private ReentrantLock lock;
/**
* Conditions to control that the buffer has lines and has empty space
*/
private Condition lines;
private Condition space;
/**
* Attribute to control where are pending lines in the buffer
*/
private boolean pendingLines;
/**
* Constructor of the class. Initialize all the objects
*
* @param maxSize
* The size of the buffer
*/
public Buffer(int maxSize) {
this.maxSize = maxSize;
buffer = new LinkedList<>();
lock = new ReentrantLock();
lines = lock.newCondition();
space = lock.newCondition();
pendingLines = true;
}
/**
* Insert a line in the buffer
*
* @param line
* line to insert in the buffer
*/
public void insert(String line) {
lock.lock();
try {
while (buffer.size() == maxSize) {
space.await();
}
buffer.offer(line);
System.out.printf("%s: Inserted Line: %d\n", Thread.currentThread()
.getName(), buffer.size());
lines.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* Returns a line from the buffer
*
* @return a line from the buffer
*/
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;
}
/**
* Establish the value of the variable
*
* @param pendingLines
*/
public void setPendingLines(boolean pendingLines) {
this.pendingLines = pendingLines;
}
/**
* Returns the value of the variable
*
* @return the value of the variable
*/
public boolean hasPendingLines() {
return pendingLines || buffer.size() > 0;
}
}
Producer.java
package com.dylan.thread.ch2.c07.task;
import com.dylan.thread.ch2.c07.utils.FileMock;
/**
* This class gets lines from the simulate file and stores them in the
* buffer, if there is space in it.
*
*/
public class Producer implements Runnable {
/**
* Simulated File
*/
private FileMock mock;
/**
* Buffer
*/
private Buffer buffer;
/**
* Constructor of the class. Initialize the objects
* @param mock Simulated file
* @param buffer Buffer
*/
public Producer (FileMock mock, Buffer buffer){
this.mock=mock;
this.buffer=buffer;
}
/**
* Core method of the producer. While are pending lines in the
* simulated file, reads one and try to store it in the buffer.
*/
@Override
public void run() {
buffer.setPendingLines(true);
while (mock.hasMoreLines()){
String line=mock.getLine();
buffer.insert(line);
}
buffer.setPendingLines(false);
}
}
Consumer.java
package com.dylan.thread.ch2.c07.task;
import java.util.Random;
/**
* This class reads line from the buffer and process it
*
*/
public class Consumer implements Runnable {
/**
* The buffer
*/
private Buffer buffer;
/**
* Constructor of the class. Initialize the buffer
* @param buffer
*/
public Consumer (Buffer buffer) {
this.buffer=buffer;
}
/**
* Core method of the consumer. While there are pending lines in the
* buffer, try to read one.
*/
@Override
public void run() {
while (buffer.hasPendingLines()) {
String line=buffer.get();
processLine(line);
}
}
/**
* Method that simulates the processing of a line. Waits 10 milliseconds
* @param line
*/
private void processLine(String line) {
try {
Random random=new Random();
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Main.java
package com.dylan.thread.ch2.c07.core;
import com.dylan.thread.ch2.c07.task.Buffer;
import com.dylan.thread.ch2.c07.task.Consumer;
import com.dylan.thread.ch2.c07.task.Producer;
import com.dylan.thread.ch2.c07.utils.FileMock;
/**
* Main class of the example
*
*/
public class Main {
/**
* Main method of the example
* @param args
*/
public static void main(String[] args) {
/**
* Creates a simulated file with 100 lines
*/
FileMock mock=new FileMock(101, 10);
/**
* Creates a buffer with a maximum of 20 lines
*/
Buffer buffer=new Buffer(20);
/**
* Creates a producer and a thread to run it
*/
Producer producer=new Producer(mock, buffer);
Thread threadProducer=new Thread(producer,"Producer");
/**
* Creates three consumers and threads to run them
*/
Consumer consumers[]=new Consumer[3];
Thread threadConsumers[]=new Thread[3];
for (int i=0; i<3; i++){
consumers[i]=new Consumer(buffer);
threadConsumers[i]=new Thread(consumers[i],"Consumer "+i);
}
/**
* Strats the producer and the consumers
*/
threadProducer.start();
for (int i=0; i<3; i++){
threadConsumers[i].start();
}
}
}
运行结果:
Mock: 101
Producer: Inserted Line: 1
Mock: 100
Producer: Inserted Line: 2
Mock: 99
Producer: Inserted Line: 3
Mock: 98
Producer: Inserted Line: 4
Mock: 97
Producer: Inserted Line: 5
Mock: 96
Producer: Inserted Line: 6
Mock: 95
Producer: Inserted Line: 7
Mock: 94
Producer: Inserted Line: 8
Mock: 93
Producer: Inserted Line: 9
Mock: 92
Producer: Inserted Line: 10
Mock: 91
Producer: Inserted Line: 11
Mock: 90
Producer: Inserted Line: 12
Mock: 89
Producer: Inserted Line: 13
Mock: 88
Producer: Inserted Line: 14
Mock: 87
Producer: Inserted Line: 15
Mock: 86
Producer: Inserted Line: 16
Mock: 85
Producer: Inserted Line: 17
Mock: 84
Producer: Inserted Line: 18
Mock: 83
Producer: Inserted Line: 19
Mock: 82
Producer: Inserted Line: 20
Mock: 81
Consumer 0: Line Readed: 19
Consumer 0: Line Readed: 18
Consumer 1: Line Readed: 17
…
Mock: 2
Consumer 1: Line Readed: 19
Consumer 2: Line Readed: 18
Consumer 2: Line Readed: 17
Producer: Inserted Line: 18
Mock: 1
Producer: Inserted Line: 19
Consumer 0: Line Readed: 18
Consumer 2: Line Readed: 17
Consumer 1: Line Readed: 16
Consumer 0: Line Readed: 15
Consumer 2: Line Readed: 14
Consumer 0: Line Readed: 13
Consumer 2: Line Readed: 12
Consumer 1: Line Readed: 11
Consumer 0: Line Readed: 10
Consumer 0: Line Readed: 9
Consumer 2: Line Readed: 8
Consumer 0: Line Readed: 7
Consumer 2: Line Readed: 6
Consumer 1: Line Readed: 5
Consumer 1: Line Readed: 4
Consumer 0: Line Readed: 3
Consumer 2: Line Readed: 2
Consumer 0: Line Readed: 1
Consumer 1: Line Readed: 0