二進制Semaphore教程和示例

semaphore是一個計數(shù)器,它保護對一個或多個共享資源的訪問。在本教程中,我們將學(xué)習(xí)如何使用二進制semaphore來控制多線程對共享資源的訪問。

Semaphores是如何工作的?

可以將semaphore想象成計數(shù)器,計數(shù)器可以遞增或遞減。用一個數(shù)字(即5)初始化semaphore。現(xiàn)在這個semaphore可以連續(xù)減少最多5次,直到計數(shù)器達到0。一旦計數(shù)器為0,您可以將其增加5次,使其最大為5。semaphore的計數(shù)器值必須始終在限制0 >= n >= 5的范圍內(nèi)(在本例中)。

顯然,semaphore不僅僅是計數(shù)器。當計數(shù)器值為0時,它們能夠使線程等待,也就是說,它可以充當具有計數(shù)器功能的鎖。

從多線程的角度來說,當一個線程想要訪問一個共享資源(由semaphore保護)時,首先,它必須獲得這個semaphore。如果semaphore的內(nèi)部計數(shù)器大于0,則semaphore遞減計數(shù)器并允許訪問共享資源。否則,如果semaphore的計數(shù)器為0,semaphore將使線程休眠,直到計數(shù)器大于0。計數(shù)器中的值為0意味著所有共享資源都被其他線程使用,因此希望使用其中一個資源的線程必須等到其中一個資源空閑出來。

當線程完成共享資源的使用后,它必須釋放semaphore,以便其他線程能夠訪問共享資源。該操作增加了semaphore的內(nèi)部計數(shù)器。

什么時候使用二進制Semaphore?

很明顯,二進制semaphore可以是0或1。這意味著二進制semaphore保護對單個共享資源的訪問,因此信號量的內(nèi)部計數(shù)器只能接受值1或0。

因此,當您需要保護對多線程訪問的單個資源的訪問時,可以使用二進制semaphore。

閱讀更多:如何在Java中使用鎖

如何使用二進制Semaphore?

為了展示二進制semaphore的用法,我們將實現(xiàn)一個打印隊列,并發(fā)任務(wù)可以使用該隊列打印它們的作業(yè)。這個打印隊列將受到二進制semaphore的保護,因此一次只能打印一個線程。

PrintingJob.java

這個類表示可以提交給打印機的獨立打印。這個類實現(xiàn)Runnable接口,這樣打印機就可以在輪到它時執(zhí)行它。

public class PrintingJob implements Runnable {
    private PrinterQueue printerQueue;

    public PrintingJob(PrinterQueue printerQueue) {
        this.printerQueue = printerQueue;
    }

    public void run() {
        System.out.printf("%s: Going to print a document\n", Thread.currentThread().getName());
        printerQueue.printJob(new Object());
    }
}

PrinterQueue.java

該類表示打印機隊列/打印機。請注意,我們將值1作為這個semaphore的構(gòu)造函數(shù)的參數(shù)傳遞,因此您正在創(chuàng)建一個二進制semaphore

public class PrinterQueue {
    private final Semaphore semaphore;

    public PrinterQueue() {
        semaphore = new Semaphore(1);
    }

    public void printJob(Object document) {
        try {
            semaphore.acquire();

            Long duration = (long) (Math.random() * 10000);
            System.out.println(Thread.currentThread().getName() + ": PrintQueue: Printing a Job during " + (duration / 1000) + " seconds :: Time - " + new Date());
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.printf("%s: The document has been printed\n", Thread.currentThread().getName());

            semaphore.release();
        }
    }
}

讓我們測試一下打印程序:

public class SemaphoreExample {
    public static void main(String[] args) {
        PrinterQueue printerQueue = new PrinterQueue();
        Thread thread[] = new Thread[10];
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(new PrintingJob(printerQueue), "Thread " + i);
        }
        for (int i = 0; i < 10; i++) {
            thread[i].start();
        }
    }
}

輸出結(jié)果為:

Thread 0: Going to print a document
Thread 9: Going to print a document
Thread 8: Going to print a document
Thread 7: Going to print a document
Thread 6: Going to print a document
Thread 5: Going to print a document
Thread 4: Going to print a document
Thread 3: Going to print a document
Thread 2: Going to print a document
Thread 1: Going to print a document
Thread 9: PrintQueue: Printing a Job during 2 seconds :: Time - Sun Mar 03 13:46:03 CST 2019
Thread 9: The document has been printed
Thread 8: PrintQueue: Printing a Job during 2 seconds :: Time - Sun Mar 03 13:46:06 CST 2019
Thread 8: The document has been printed
Thread 6: PrintQueue: Printing a Job during 4 seconds :: Time - Sun Mar 03 13:46:09 CST 2019
Thread 6: The document has been printed
Thread 7: PrintQueue: Printing a Job during 4 seconds :: Time - Sun Mar 03 13:46:13 CST 2019
Thread 7: The document has been printed
Thread 5: PrintQueue: Printing a Job during 7 seconds :: Time - Sun Mar 03 13:46:17 CST 2019
Thread 5: The document has been printed
Thread 4: PrintQueue: Printing a Job during 9 seconds :: Time - Sun Mar 03 13:46:25 CST 2019
Thread 4: The document has been printed
Thread 3: PrintQueue: Printing a Job during 4 seconds :: Time - Sun Mar 03 13:46:35 CST 2019
Thread 3: The document has been printed
Thread 2: PrintQueue: Printing a Job during 1 seconds :: Time - Sun Mar 03 13:46:39 CST 2019
Thread 2: The document has been printed
Thread 0: PrintQueue: Printing a Job during 5 seconds :: Time - Sun Mar 03 13:46:41 CST 2019
Thread 0: The document has been printed
Thread 1: PrintQueue: Printing a Job during 1 seconds :: Time - Sun Mar 03 13:46:46 CST 2019
Thread 1: The document has been printed

查看printJob()方法。此方法展示了在使用semaphore實現(xiàn)臨界區(qū)和保護對共享資源的訪問時必須遵循的三個步驟:

  1. 首先,使用acquire()方法獲取semaphore。
  2. 然后,對共享資源執(zhí)行必要的操作。
  3. 最后,使用release()方法釋放semaphore

Semaphore類的構(gòu)造函數(shù)中允許第二個參數(shù)。此參數(shù)必須采用布爾值。如果您給它false值,您就創(chuàng)建了一個semaphore,它將在非公平模式下工作。這是默認選項。如果您給它true值,您就創(chuàng)建了一個semaphore,它將在公平模式下工作。

參考資料
Binary Semaphore Tutorial and Example

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,631評論 1 32
  • 在一個方法內(nèi)部定義的變量都存儲在棧中,當這個函數(shù)運行結(jié)束后,其對應(yīng)的棧就會被回收,此時,在其方法體中定義的變量將不...
    Y了個J閱讀 4,568評論 1 14
  • 1. 基礎(chǔ)知識 1.1、 基本概念、 功能 馮諾伊曼體系結(jié)構(gòu)1、計算機處理的數(shù)據(jù)和指令一律用二進制數(shù)表示2、順序執(zhí)...
    yunpiao閱讀 5,780評論 1 22
  • 時間:2016年2月 整體說明 項目名稱 神農(nóng)耕 采用技術(shù) 項目描述 神農(nóng)耕手機版(神農(nóng)耕農(nóng)業(yè)信息服務(wù)平臺)是一款...
    closefrien_d1c2閱讀 658評論 0 0
  • 一簇星光半點亂, 思前想后總夜半, 春風(fēng)不解回頭月, 山人望天自長嘆!
    今古傳奇吳總閱讀 476評論 1 19

友情鏈接更多精彩內(nèi)容