(上一節的繼續)

2.2 使用Lock機制

        Java提供了另外一種機制來同步程式碼塊。它是比synchrozied關鍵字更為強大且彈性的機制。它是基於鎖介面和實現了這個介面的類(如ReetrantLock類)。這個機制表現出一些優勢,如下所示:

1.       以一種更加彈性的方式允許同步塊結構,而使用synchrozied關鍵字,你必須以一個機構化的方式來獲取和釋放對於同步塊程式碼的控制。實用Lock介面允許你獲取更為複雜的結構去實現你的臨界部分。

2.       Lock介面提供除synchronized外額外的功能,新功能之一就是它實現了tryLock()方法。這個方法嘗試著去獲取鎖的控制,如果它不能夠獲取(已經被其它的執行緒佔有),它返回這個鎖。Synchronized關鍵字修飾的方法中,當執行緒A嘗試著去執行synchronized程式碼塊時,而這個synchronized程式碼塊已經被執行緒B所執行,這時,執行緒A不得不掛起直到執行緒B完成它的同步塊程式碼的執行。在Lock實現中,有可以執行tryLock方法,它返回一個布林值來顯示是否那兒有另外的執行緒執行這個受Lock保護的程式碼。

3.       Lock介面允許讀寫操作分離,有多個讀者和唯一的寫者。

4.       Lock介面比synchronized關鍵字提供了更好的效能。

看下面這個例子,比較好闡明這Lock的使用。

定義印表機佇列任務,列印服務PrintQueue類。

public class PrintQueue {
 
 
    /**
     * Lock to control the access to the queue.
     */
    private finalLock queueLock=new ReentrantLock();
   
    /**
     * Method that prints a document
     * @param document document to print
     */
    public voidprintJob(Object document){
        queueLock.lock();
       
        try {
            Long duration=(long)(Math.random()*10000);
            System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n",Thread.currentThread().getName(),(duration/1000));
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }
    }
 
}

定義列印呼叫類Task,呼叫對應的列印任務。

public class Task implements Runnable {
    /**
     * Queue to print the documents
     */
    private PrintQueue printQueue;
   
    /**
     * Constructor of the class. Initializes thequeue
     * @param printQueue
     */
    public Task(PrintQueue printQueue){
        this.printQueue=printQueue;
    }
   
    /**
     * Core method of the Job. Sends the documentto the print queue and waits
     *  forits finalization
     */
    @Override
    public voidrun() {
        System.out.printf("%s: Going to print a document\n",Thread.currentThread().getName());
        printQueue.printJob(new Object());
        System.out.printf("%s: The document has been printed\n",Thread.currentThread().getName());     
    }
    /**
     * @param args
     */
    public staticvoidmain(String[] args) {
        // Creates the print queue
        PrintQueue printQueue=new PrintQueue();
       
        // Creates ten Threads
        Thread thread[]=new Thread[10];
        for (int i=0; i<10; i++){
            thread[i]=new Thread(new Task(printQueue),"Thread "+i);
        }
       
        // Starts the Threads
        for (int i=0; i<10; i++){
            thread[i].start();
        }
 
    }
 
}

執行結構如下:

Thread1: Going to print a document
Thread9: Going to print a document
Thread8: Going to print a document
Thread7: Going to print a document
Thread6: Going to print a document
Thread5: Going to print a document
Thread4: Going to print a document
Thread0: Going to print a document
Thread3: Going to print a document
Thread2: Going to print a document
Thread1: PrintQueue: Printing a Job during 9 seconds
Thread1: The document has been printed
Thread9: PrintQueue: Printing a Job during 8 seconds
Thread9: The document has been printed
Thread8: PrintQueue: Printing a Job during 5 seconds
Thread8: The document has been printed
Thread7: PrintQueue: Printing a Job during 2 seconds
Thread7: The document has been printed
Thread6: PrintQueue: Printing a Job during 0 seconds
Thread6: The document has been printed
Thread5: PrintQueue: Printing a Job during 5 seconds
Thread5: The document has been printed
Thread4: PrintQueue: Printing a Job during 2 seconds
Thread4: The document has been printed
Thread0: PrintQueue: Printing a Job during 7 seconds
Thread0: The document has been printed
Thread3: PrintQueue: Printing a Job during 2 seconds
Thread3: The document has been printed
Thread2: PrintQueue: Printing a Job during 7 seconds
Thread 2: The document has been printed