3.5 在一个锁中使用多种状态
一个锁(Lock)可能和一个或者多个状态相关联,这些状态在Condition接口中已经被声明好了。这些状态的作用就是去运行线程控制一个锁或者检查一个状态是否为true或者false。如果是false,这个线程将被挂起直到其它的线程将它们唤醒。这个Condition接口提供了特定的机制去挂起一个线程和唤醒一个挂起的线程。
在并发编程中,一个金典的问题就是生产-消费问题。我们有一个数据缓存,一个或者多个数据的生产者,它们负责保持数据到缓存中,一个或者多个从缓存中取数据的消费者。
下面介绍这个实例来阐明如何解决生产消费问题。
定义一个Buffer类:
/** * This class implements a buffer tostores the simulate file lines between the * producer and the consumers * */ public class Buffer { /** * The buffer */ private LinkedList<String> buffer; /** * Size of the buffer */ private intmaxSize; /** * Lock to control the access to the buffer */ private ReentrantLock lock; /** * Conditions to control that the buffer haslines and has empty space */ private Condition lines; private Condition space; /** * Attribute to control where are pending linesin the buffer */ private booleanpendingLines; /** * Constructor of the class. Initialize all theobjects * * @param maxSize * The size of the buffer */ public Buffer(intmaxSize) { this.maxSize = maxSize; buffer = new LinkedList<String>(); 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 voidinsert(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 voidsetPendingLines(boolean pendingLines) { this.pendingLines = pendingLines; } /** * Returns the value of the variable * * @return the value of the variable */ public booleanhasPendingLines() { return pendingLines || buffer.size() > 0; } }
定义工具类FileMock,模拟文本文件。
/** * This class simulates a text file.It creates a defined number * of random lines to process themsequentially. * */ public class FileMock { /** * Content of the simulate file */ private String content[]; /** * Number of the line we are processing */ private intindex; /** * Constructor of the class. Generate therandom data of the file * @param size: Number of lines in the simulate file * @param length: Length of the lines */ public FileMock(intsize, intlength){ 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 toprocess or false if not * @return true if the file has more lines to process or false ifnot */ public booleanhasMoreLines(){ return index<content.length; } /** * Returns the next line of the simulate fileor 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; } }
定义生产类Producer:
/** * This class gets lines from thesimulate 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 theobjects * @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 arepending lines in the * simulated file, reads one and try to storeit in the buffer. */ @Override public voidrun() { buffer.setPendingLines(true); while (mock.hasMoreLines()){ String line=mock.getLine(); buffer.insert(line); } buffer.setPendingLines(false); } }
定义消费类Consumer:
import java.util.Random; /** * This class reads line from thebuffer and process it * */ public class Consumer implements Runnable { /** * The buffer */ private Buffer buffer; /** * Constructor of the class. Initialize thebuffer * @param buffer */ public Consumer (Buffer buffer) { this.buffer=buffer; } /** * Core method of the consumer. While there arepending lines in the * buffer, try to read one. */ @Override public voidrun() { while (buffer.hasPendingLines()) { String line=buffer.get(); processLine(line); } } /** * Method that simulates the processing of aline. Waits 10 milliseconds * @param line */ private voidprocessLine(String line) { try { Random random=new Random(); Thread.sleep(random.nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } }
测试类ProducerConsumerTest:
public class ProducerConsumerTest { /** * Main method of the example * @param args */ public staticvoidmain(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 runthem */ 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); } /** * Starts 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 Consumer0: Line Readed: 2 Consumer1: Line Readed: 1 Consumer2: Line Readed: 0 Producer:Inserted Line: 1 Mock:97 Consumer2: Line Readed: 0 Producer:Inserted Line: 1 Mock:96 Producer:Inserted Line: 2 Mock:95 Producer:Inserted Line: 3 Mock:94 Producer:Inserted Line: 4 Mock:93 Producer:Inserted Line: 5 Mock:92 Producer:Inserted Line: 6 Mock:91 Producer:Inserted Line: 7 Mock:90 Producer:Inserted Line: 8 Mock:89 Producer:Inserted Line: 9 Mock:88 Producer:Inserted Line: 10 Mock:87 Producer:Inserted Line: 11 Mock:86 Producer:Inserted Line: 12 Mock:85 Producer:Inserted Line: 13 Mock:84 Producer:Inserted Line: 14 Mock:83 Producer:Inserted Line: 15 Mock:82 Producer:Inserted Line: 16 Mock:81 Producer:Inserted Line: 17 Mock:80 Producer:Inserted Line: 18 Mock:79 Producer:Inserted Line: 19 Mock:78 Producer:Inserted Line: 20 Mock:77 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:76 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:75 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:74 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:73 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:72 Consumer1: Line Readed: 19 Consumer0: Line Readed: 18 Producer:Inserted Line: 19 Mock:71 Producer:Inserted Line: 20 Mock:70 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:69 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:68 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:67 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:66 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:65 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:64 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:63 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:62 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:61 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:60 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:59 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:58 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:57 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:56 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:55 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:54 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:53 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:52 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:51 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:50 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:49 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:48 Consumer0: Line Readed: 19 Consumer2: Line Readed: 18 Producer:Inserted Line: 19 Mock:47 Producer:Inserted Line: 20 Mock:46 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:45 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:44 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:43 Consumer1: Line Readed: 19 Consumer1: Line Readed: 18 Producer:Inserted Line: 19 Mock:42 Producer:Inserted Line: 20 Mock:41 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:40 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:39 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:38 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:37 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:36 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:35 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:34 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:33 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:32 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:31 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:30 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:29 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:28 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:27 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:26 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:25 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:24 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:23 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:22 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:21 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:20 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:19 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:18 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:17 Consumer1: Line Readed: 19 Consumer2: Line Readed: 18 Producer:Inserted Line: 19 Mock:16 Producer:Inserted Line: 20 Mock:15 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:14 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:13 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:12 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:11 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:10 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:9 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:8 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:7 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Mock:6 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:5 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:4 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:3 Consumer0: Line Readed: 19 Producer:Inserted Line: 20 Mock:2 Consumer1: Line Readed: 19 Producer:Inserted Line: 20 Mock:1 Consumer2: Line Readed: 19 Producer:Inserted Line: 20 Consumer0: Line Readed: 19 Consumer1: Line Readed: 18 Consumer2: Line Readed: 17 Consumer0: Line Readed: 16 Consumer2: Line Readed: 15 Consumer2: Line Readed: 14 Consumer0: Line Readed: 13 Consumer2: Line Readed: 12 Consumer1: Line Readed: 11 Consumer1: Line Readed: 10 Consumer0: Line Readed: 9 Consumer2: Line Readed: 8 Consumer1: Line Readed: 7 Consumer2: Line Readed: 6 Consumer2: Line Readed: 5 Consumer0: Line Readed: 4 Consumer2: Line Readed: 3 Consumer1: Line Readed: 2 Consumer0: Line Readed: 1 Consumer0: Line Readed: 0