Semaphore的使用

Semaphore的使用

關注

Semaphore 簡介

本文將對Semaphore類中的全部方法進行案例式的實驗,這樣可以全面地瞭解此類提供類哪些核心功能。

此類的主要作用就是限制線程併發的數量,如果不限制線程併發的數量,CPU資源很快就會被耗盡,每個線程執行的任務是相當緩慢,因為CPU把時間片分配給不同的線程對象,而且上下文切換也要耗時,最終造成系統運行效率大幅降低,所以限制併發線程的數量還是非常有必要的。

Semaphore的同步性

類Semaphore所提供的功能完全就是sybchronized關鍵字的升級版,但它提供的功能更加的強大與方便,主要作用就是控制線程併發的數量,而這一點,單純地使用synchronized是做不到的。

我們首先通過一個初步的案例來看看Semaphore類是如何實現限制線程併發數的。

Semaphore的使用

圖1

Thread-4 1522991382314開始執行!

Thread-4 1522991383318執行結束!

Thread-3 1522991383319開始執行!

Thread-3 1522991384324執行結束!

Thread-0 1522991384325開始執行!

Thread-0 1522991385327執行結束!

Thread-1 1522991385327開始執行!

Thread-1 1522991386329執行結束!

Thread-2 1522991386329開始執行!

Thread-2 1522991387331執行結束!

類Semaphore的構造函數參數permits是許可的意思,代表同一時間內,最多允許多少個線程同時執行acquire()和release()之間的代碼。所以上面的示例中,最多允許1個線程執行acquire()和release()之間的代碼,所以打印的結果就是5個線程是同步的。

其實還可以傳入>1的許可,代表同一時間內,最多允許x個線程可以執行acquire()和release()之間的代碼。

如果將上面示例中Semaphore的構造函數參數permits改為2:

Semaphore的使用

圖2

執行結果:

Thread-0 1522991932240開始執行!

Thread-4 1522991932240開始執行!

Thread-4 1522991933244執行結束!

Thread-0 1522991933244執行結束!

Thread-3 1522991933245開始執行!

Thread-1 1522991933245開始執行!

Thread-3 1522991934248執行結束!

Thread-1 1522991934248執行結束!

Thread-2 1522991934248開始執行!

Thread-2 1522991935253執行結束!

需要說明一下,對Semaphore類的構造方法傳遞的參數permits值如果大於1時,該類並不能保證線程安全性,因為還是有可能出現多個線程共同訪問實例變量,導致出現髒數據的情況。

方法acquire(int permits)參數作用及動態添加permits許可數量

有參方法acquire(int permits)功能是沒調用一次此方法,就使用x個許可。無參的acquire()作用是使用1個許可。

Semaphore的使用

圖3

執行結果:

Thread-2 1522993586620開始執行!

Thread-9 1522993586620開始執行!

Thread-6 1522993586620開始執行!

Thread-0 1522993586620開始執行!

Thread-1 1522993586620開始執行!

Thread-2 1522993587096執行結束!

Thread-8 1522993587097開始執行!

Thread-0 1522993587212執行結束!

Thread-8 1522993587212執行結束!

Thread-5 1522993587213開始執行!

Thread-3 1522993587212開始執行!

Thread-6 1522993587361執行結束!

Thread-4 1522993587362開始執行!

Thread-9 1522993587531執行結束!

Thread-7 1522993587531開始執行!

Thread-1 1522993587557執行結束!

Thread-3 1522993587949執行結束!

Thread-4 1522993587991執行結束!

Thread-5 1522993588139執行結束!

Thread-7 1522993588158執行結束!

代碼中有10個許可,每次執行代碼semaphore.acquire(2);時消耗掉2個,所有10/2=5,說明同一時間只有5個線程允許執行acquire()和release()之間的代碼。

Semaphore類構造參數new Semaphore(5);中的5並不是最終的許可數,僅僅是初始的狀態值。release(int permits),可以動態添加permits的個數,無參release()作用是添加1個許可。

Semaphore的使用

圖4

執行結果:

0

6

10

方法availablePermits()和drainPermits()

availablePermits()返回此Semaphore對象中當前可用的許可數,此方法通常用於調試,因為許可的數量可能實時在改變,並不是固定的數量。

drainPermits()可獲取並返回立即可用的所有許可個數,並且將可用許可置0。

Semaphore的使用

圖5

執行結果:

3

3

0 0

方法acquireUninterruptibly()的使用

方法acquireUninterruptibly()的作用是使等待進入acquire()方法的線程,不允許被中斷。

Semaphore的使用

圖6

執行結果:

A開始執行!

main……end!

java.lang.InterruptedException

at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:998)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)

at java.util.concurrent.Semaphore.acquire(Semaphore.java:312)

at com.thread.Semaphore.SemaphoreTest$Service.test(SemaphoreTest.java:17)

at com.thread.Semaphore.SemaphoreTest$MyThread.run(SemaphoreTest.java:36)

A執行結束!

上面示例未使用acquireUninterruptibly()所以,在現在B等待進入許可的時候被成功中斷了。如果將示例中代碼改成使用acquireUninterruptibly():

semaphore.acquireUninterruptibly();

執行結果:

A開始執行!

main……end!

A執行結束!

B開始執行!

B執行結束!

acquireUninterruptibly()方法還有重載的寫法acquireUninterruptibly(int permits),此方法的作用是在等待許可的情況下不允許中斷,如果成功獲得鎖,則取得制定的permits許可個數。

公平與非公平信號量測試

所謂的公平信號量是獲得鎖的順序與線程啟動的順序有關,但不代表100%地獲得信號量,僅僅是在概率上能得到保證。而非公平信號量就是無關的量。

Semaphore的使用

圖7

執行結果

Thread-2啟動了!

Thread-0啟動了!

Thread-1啟動了!

Thread-4啟動了!

Thread-3啟動了!

Thread-2開始執行!

Thread-5啟動了!

Thread-5開始執行!

Thread-1開始執行!

Thread-0開始執行!

Thread-4開始執行!

Thread-3開始執行!

更改Service代碼如下:

private boolean isFair = true;

執行結果:

Thread-1啟動了!

Thread-4啟動了!

Thread-1開始執行!

Thread-2啟動了!

Thread-0啟動了!

Thread-3啟動了!

Thread-5啟動了!

Thread-4開始執行!

Thread-2開始執行!

Thread-0開始執行!

Thread-3開始執行!

Thread-5開始執行!

由此可見,非公平信號量允許的效果是線程啟動的順序與調用semaphore.acquire();的順序無關,也就是線程先啟動並不代表先獲得許可。公平信號量的運行效果是線程啟動的順序與調用semaphore.acquire();的順序有關,也就是先啟動的線程優先獲得許可。

方法tryAcquire()的使用

無參方法tryAcquire()的作用是嘗試地獲得1個許可,如果獲取不到則返回false,此方法通常與if語句結合使用,其具有無阻塞的特點。無阻塞的特點可以使線程不至於在同步處一直持續等待的狀態,如果if語句判斷不成立則線程會繼續走else語句,程序會繼續向下運行。

Semaphore的使用

執行結果:

A開始執行!

B未成功進入!

  • tryAcquire(int permits)的作用是嘗試地獲得x個許可,如果獲取不到則返回false。

  • tryAcquire(long timeout,TimeUnit unit)的作用是在指定的時間內嘗試地獲得1個許可,如果獲取不到則返回false。

  • tryAcquire(int permits,long timeout,TimeUnit unit)的作用是在指定時間內嘗試地獲得x個許可,如果獲取不到則返回false

Semaphore的使用

結束

絕對乾貨!!!持續更新!!!

如果您喜歡請加關注!!!


分享到:


相關文章: