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