Java併發編程之Semaphore源碼分析

Semaphore介紹

Semaphore是JDK1.5提供容許一組拿到許可證的線程訪問共享資源,並禁止其餘拿不到許可證的線程訪問共享資源工具。Semaphore通常用來作系統的限流。java

特色

Semaphore和ReentrantLock功能很相似都是限制線程訪問共享資源並且都有公平鎖和非公平鎖模式。不一樣點以下表格:工具

Semaphore ReentrantLock
容許線程訪問共享資源個數 可配多個 1個
可重入

Semaphore原理分析

Semaphore的實現是ReentrantLock和CyclicBarrier的結合體,不少源碼跟ReentrantLock和CyclicBarrier同樣。源碼分析

acquire()源碼分析

acquire()源碼以下ui

public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

acquire()是直接調用了AQS裏的acquireSharedInterruptibly(1)方法咱們來看下acquireSharedInterruptibly(1)方法.net

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
	//判斷當前線程是否被中斷,中斷的話拋出異常
        if (Thread.interrupted())
            throw new InterruptedException();
	//判斷當前計數器是否爲0是的話返回1不然返回-1
        if (tryAcquireShared(arg) < 0)
	//加入到同步阻塞隊列等待被喚醒
            doAcquireSharedInterruptibly(arg);
    }

先判斷當前線程是否被中斷,中斷的話拋出異常。而後調用tryAcquireShared(arg)減許可證並返回剩餘許可證,這裏若是許可證爲0了就表示許可證已經用完須要進行阻塞等待,不然獲取到許可證,則調用 doAcquireSharedInterruptibly(arg)加入到同步阻塞隊列等待被喚醒。線程

tryAcquireShared(arg)的實現分公平模式和非公平模式,先看下默認非公平模式的實現:code

protected int tryAcquireShared(int acquires) {
     return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
			//獲取當前許可證
                int available = getState();
			//減許可證
                int remaining = available - acquires;
			//若是小於0直接返回不然CAS替換剩餘值
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

實現很簡單就是減掉一個許可證,並返回剩餘許可證。blog

再看下公平模式的實現:隊列

protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

該方法跟非公平鎖基本都同樣,只是在獲取鎖的時候加了hasQueuedPredecessors()判斷,這個方法主要判斷了當前線程是否在頭節點的下個節點,這樣保證了獲取鎖的順序性。資源

doAcquireSharedInterruptibly(arg)方法在以前文章已經講過,這裏再也不累述連接

release()源碼分析

public void release() {
   sync.releaseShared(1);
}

release()是直接調用了AQS裏的releaseShared(1)方法咱們來看下releaseShared(1)方法

releaseShared(1)源碼以下

public final boolean releaseShared(int arg) {
 		//許可證加1並返回是否成功
        if (tryReleaseShared(arg)) {
		//喚醒同步阻塞隊列中的頭節點的下個節點線程
            doReleaseShared();
            return true;
        }
        return false;
    }

先調用tryReleaseShared(arg)將許可證加1並返回是否成功,若是是調用doReleaseShared()喚醒同步阻塞隊列中的頭節點的下個節點線程。

下面來看下tryReleaseShared(arg)方法

protected final boolean tryReleaseShared(int releases) {
            for (;;) {
		//獲取當前許可證
                int current = getState();
		//獲取許可證加releases
                int next = current + releases;
		//加了之後的值比原來的值小,說明releases傳的是負數,直接拋出異常
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
		//CAS替換原來的值
                if (compareAndSetState(current, next))
                    return true;
            }
        }

實現很簡單就是增長一個許可證,並用CAS替換掉原來的值,若是失敗自旋直至成功。

doReleaseShared()方法在以前文章已經講過,這裏再也不累述連接

相關文章
相關標籤/搜索