使用这些条件的目的是去控制一个锁并且可以检查一个条件是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