【詳解】JUC之Semaphore

簡介

Semaphore是一種在多線程環境下使用的設施,該設施負責協調各個線程,以保證它們能夠正確、合理的使用公共資源的設施,也是操作系統中用於控制進程同步互斥的量。

Semaphore是一種計數信號量,用於管理一組資源,內部是基於AQS的共享模式。它相當於給線程規定一個量從而控制允許活動的線程數

Semaphore用於限制可以訪問某些資源(物理或邏輯的)的線程數目,他維護了一個許可證集合,有多少資源需要限制就維護多少許可證集合,假如這裏有N個資源,那就對應於N個許可證,同一時刻也只能有N個線程訪問。

舉個例子:
相信在學生時代都去餐廳打過飯,假如有3個窗口可以打飯,同一時刻也只能有3名同學打飯。第四個人來了之後就必須在外面等着,只要有打飯的同學好了,就可以去相應的窗口了。

一個線程獲取許可證就調用acquire方法,用完了釋放資源就調用release方法。

自定義Lock

public class SemaphoreTest {

    public static void main(String[] args) {
        final SemaphoreLock lock = new SemaphoreLock();
        for (int i = 0; i < 2; i++) {
            new Thread(()->{
                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + " get the lock.");
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    System.out.println(Thread.currentThread().getName() + " release the lock.");
                    lock.unlock();
                }
            }).start();
        }

    }



    static class SemaphoreLock{
        private final Semaphore semaphore = new Semaphore(1);

        public void lock() throws InterruptedException {
            semaphore.acquire();
        }

        public void unlock()  {
            semaphore.release();
        }
    }
}

結果

Thread-0 get the lock.
Thread-0 release the lock.
Thread-1 get the lock.
Thread-1 release the lock.

跟synchronized的區別

  • 可以看出最大的區別就是可以控制多個線程訪問資源,而不是隻用一個線程

構造方法

public Semaphore(int permits)
public Semaphore(int permits , boolean fair)
  • permits:同一時間可以訪問資源的線程數目
  • fair:儘可能的保證公平

重要方法

public void acquire() throws InterruptedException
public void release()
  • acquire()獲取一個許可證,如果許可證用完了,則陷入阻塞。可以被打斷。
  • release()釋放一個許可證
acquire(int permits) 
public void release(int permits)

acquire多個時,如果沒有足夠的許可證可用,那麼當前線程將被禁用以進行線程調度,並且處於休眠狀態,直到發生兩件事情之一:

  • 一些其他線程調用此信號量的一個release方法,當前線程旁邊將分配許可證,並且可用許可證的數量滿足此請求;
  • 要麼一些其他線程interrupts當前線程。

release多個時,會使許可證增多,最終可能超過初始值

public boolean tryAcquire(int permits)
public boolean tryAcquire(int permits,
                          long timeout,
                          TimeUnit unit)
                   throws InterruptedException
  • 嘗試去拿,拿到返回true

其他方法

public int availablePermits()
  • 返回此信號量中當前可用的許可數。
protected Collection<Thread> getQueuedThreads()
public final int getQueueLength()
  • getQueuedThreads返回正在阻塞的線程集合
  • getQueueLength返回阻塞獲取的線程數
public void acquireUninterruptibly()
public void acquireUninterruptibly(int permits)
  • 可以不被打斷獲取許可證
public int drainPermits()
  • 獲取當前全部的許可證
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章