3執行緒同步實用程式

在這一節中,我們將討論如何使用高級別機制去獲得多執行緒的同步(synchronized)。這些高級別機制有下面幾種:

Ø  訊號(Semaphores):一個訊號就是一個計數器,它控制著對於一個或者多個共享資源的訪問。這個機制是併發程式設計的基本工具,在大多數程式語言中都提供這樣的機制。

Ø  倒計時彈簧鎖:CountDownLatch類是Java語言提供的一種機制,允許一個執行緒去等待多個操作行為的結束。

Ø  迴圈阻塞器:CyclicBarrier類是Java語言提供的一種機制,允許在一個普通點上同步多個執行緒。

Ø  分階分離器(Phaser):Phaser類是Java API 7語言中提供的一種機制。它控制著併發程式在多個階段來執行。這是Java API 7 中的新特性。

Ø  交換器(Exchanger):交換器類是Java語言提供的另外的機制,提供給兩個執行緒一個交換資料點。

3.1 控制併發地訪問一個資源

Semaphore類有兩個 acquire()方法的另外版本:

1.     acqureUninterruptibly():當這個內部的計數器的訊號為0,阻塞這個執行緒直到這個訊號被釋放。在這個阻塞時間內,執行緒可能會被中斷。然後,這個方法報出InterruptedException異常。這個版本的acquire將忽略掉執行緒的中斷,不會報出任何異常。

2.     tryAcqure():這個方法將嘗試著獲取訊號(semaphore)。如果它獲得了,這個方法返回true值。但是,如果它不能夠,這個方法返回false值,而不會被阻塞並等待訊號的釋放。程式設計師負責根據這個返回值而定義正確的業務邏輯。

下面列舉一個例子,說明semaphore的使用。例子中定義列印佇列服務,多執行緒PrintJob呼叫列印服務。

定義PrintQueueTask類:

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
 
/**
 * This class implements thePrintQueue using a Semaphore to control the
 * access to it.
 *
 */
public class PrintQueueTask {
   
    /**
     * Semaphore to control the access to the queue
     */
    private finalSemaphore semaphore;
   
    /**
     * Constructor of the class. Initializes thesemaphore
     */
    public PrintQueueTask(){
        semaphore=new Semaphore(1);
    }
   
    /**
     * Method that simulates printing a document
     * @param document Document to print
     */
    public voidprintJob (Object document){
        try {
            // Get the access to the semaphore. If other job is printing, this
            // thread sleep until get the access to the semaphore
            semaphore.acquire();
           
            Long duration=(long)(Math.random()*5);
            System.out.printf("%s: PrintQueuesTask: Printing a Job during %d seconds\n",Thread.currentThread().getName(),duration);
            Thread.sleep(duration);        
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // Free the semaphore. If there are other threads waiting for thissemaphore,
            // the JVM selects one of this threads and give it the access.
            semaphore.release();           
        }
    }
 
}
 

定義執行類PrintJob:

/**
 * This class simulates a job thatsend a document to print.
 *
 */
public class PrintJob implements Runnable {
 
    /**
     * Queue to print the documents
     */
    private PrintQueueTask printQueueTask;
   
    /**
     * Constructor of the class. Initializes thequeue
     * @param printQueueTask
     */
    public PrintJob(PrintQueueTask printQueueTask){
        this.printQueueTask=printQueueTask;
    }
   
    /**
     * Core method of the Job. Sends the documentto the print queue and waits
     *  forits finalization
     */
    @Override
    public void run() {
        System.out.printf("%s: Going to print a job\n",Thread.currentThread().getName());
        printQueueTask.printJob(new Object());
        System.out.printf("%s: The document has been printed\n",Thread.currentThread().getName());     
    }/**
     * Main method of the class. Run ten jobs inparallel that
     * send documents to the print queue at thesame time.
     */
    public static void main (String args[]){
       
        // Creates the print queue
        PrintQueueTask printQueue=new PrintQueueTask();
       
        // Creates ten Threads
        Thread thread[]=new Thread[10];
        for (int i=0; i<10; i++){
            thread[i]=new Thread(new PrintJob(printQueue),"Thread "+i);
        }
       
        // Starts the Threads
        for (int i=0; i<10; i++){
            thread[i].start();
        }
    }
   
   
}

執行結果:

Thread0: Going to print a job
Thread9: Going to print a job
Thread8: Going to print a job
Thread7: Going to print a job
Thread6: Going to print a job
Thread5: Going to print a job
Thread4: Going to print a job
Thread3: Going to print a job
Thread2: Going to print a job
Thread1: Going to print a job
Thread0: PrintQueuesTask: Printing a Job during 1 seconds
Thread0: The document has been printed
Thread9: PrintQueuesTask: Printing a Job during 3 seconds
Thread9: The document has been printed
Thread8: PrintQueuesTask: Printing a Job during 4 seconds
Thread7: PrintQueuesTask: Printing a Job during 3 seconds
Thread8: The document has been printed
Thread7: The document has been printed
Thread6: PrintQueuesTask: Printing a Job during 2 seconds
Thread6: The document has been printed
Thread5: PrintQueuesTask: Printing a Job during 4 seconds
Thread5: The document has been printed
Thread4: PrintQueuesTask: Printing a Job during 2 seconds
Thread3: PrintQueuesTask: Printing a Job during 3 seconds
Thread4: The document has been printed
Thread3: The document has been printed
Thread2: PrintQueuesTask: Printing a Job during 0 seconds
Thread2: The document has been printed
Thread1: PrintQueuesTask: Printing a Job during 3 seconds
Thread 1: The document has been printed