【java併發】Semaphore源碼解析

一、概述

Semaphore信號量,主要功能是限制能夠運行的線程量,指定固定數量的許可,只有獲取許可的線程可以運行,若沒有許可獲取則阻塞,直到獲取許可。

二、源碼分析

1、構造函數

    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and nonfair fairness setting.
     *	創建給定許可證數量信號量和非公平設置
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     * 初始許可證數量.
     * 這個值允許爲負數,在這種情況下必須在任何獲取之前釋放
     */
	public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    /**
    * @param fair {@code true} 保證競爭先進先出
    */
	public Semaphore(int permits, boolean fair) {
	        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

Semaphore中同步器Sync繼承AQS實現AQS中的共享模式tryReleaseShared(int releases)方法,同步器只實現了共享鎖的釋放,並未實現鎖的獲取tryAcquireShared(int arg)方法;
Semaphore中公平和非公平是通過它的兩個私有的靜態內部類FairSync、NonFairSync實現,他們通過繼承Sync並實現共享鎖的獲取tryAcquireShared(int arg)方法;
非公平實現:

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;

        NonfairSync(int permits) {
            super(permits);
        }
		// 只是實現了AQS中的共享鎖的獲取
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

公平實現:

    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;

        FairSync(int permits) {
            super(permits);
        }
		// 只是實現了AQS中的共享鎖的獲取
        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;
            }
        }
    }

2、void acquire()

這個方法是從信號量獲取一個許可,在獲取到許可,或線程中斷之前,當前線程阻塞;獲取許可後立即返回並將許可數減一

	/**
	* 如果沒有許可可用,則會休眠知道發生以下兩種情況
	* 1、其他調用release方法釋放許可,並且當前線程獲取到許可
	* 2、其他線程中斷了當前線程
	* 	1)當前線程在進入這個方法時設置了中斷標誌位
	* 	2)等待許可時發生了中斷,則拋出中斷異常
	*/
 	public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

這個方法是直接調用AQS的acquireSharedInterruptibly(int ard)方法;
方法實現原理請查看AQS源碼:

	/**
	* 首先檢測是否中斷.中斷後拋出異常
	* 嘗試獲取許可,成功退出;失敗則進入AQS隊列,直至成功獲取或中斷
	*/
	public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 嘗試獲取鎖,返回剩餘共享鎖的數量;小於0則加入同步隊列,自旋
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

tryAcquireShared(arg)則會調用Semaphore中兩個同步器的實現方法;
如果獲取失敗則加入隊列等待喚醒;

非公平模式的實現

非公平實現都是首先查看是否有可獲取的許可,如果有則獲取成功,沒有則進隊列等待;利用此可以提高併發量,但可能導致某些線程長時間獲取不到許可,造成線程‘餓死’。

	protected int tryAcquireShared(int acquires) {
       return nonfairTryAcquireShared(acquires);
    }

直接調用其父類Sync中非公平共享獲取

	final int nonfairTryAcquireShared(int acquires) {
		// 自旋直到無許可或者狀態位賦值成功
        for (;;) {
            int available = getState();
            int remaining = available - acquires;
            // 如果小於0則直接返回,否則利用CAS給AQS狀態位賦值
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }
公平模式的實現

公平與非公平的區別在於始終按照AQS隊列FIFO的順序來的

protected int tryAcquireShared(int acquires) {
	//自旋 CAS 實現線程安全
    for (;;) {
    	// 判斷是否有前置任務排隊
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

以上兩種模式獲取失敗後都會調用doAcquireSharedInterruptibly(int arg);自旋等待獲取鎖 。源碼解析請看AQS解析

3、void release()

公平和非公平使用相同的釋放
釋放許可

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

調用AQS中的releaseShared(int arg)

    public final boolean releaseShared(int arg) {
    	// 調用Sync實現的tryReleaseShared
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

tryReleaseShared(int arg)是父類Sync實現。自旋直到釋放成功

        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

如果釋放許可成功,則調用AQS中的doReleaseShared()方法來喚醒AQS隊列中等待的線程
源碼解析請看AQS解析

這裏就是Semaphore的主要源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章