Semaphore源碼閱讀

1.作用說明

網上有一些比較好的解釋,直接截圖:
https://www.jianshu.com/p/2f1d6942bcd5
https://www.jianshu.com/p/2f1d6942bcd5
關於Semaphore,他的中文解釋是信號量,可以舉例子來理解一下這個東西:
銀行有兩個辦事窗口,但是這個時候可能有十個人在等待,那麼等待的十個人需要等待窗口空閒了才能夠進行業務辦理.這個時候銀行窗口就是信號量,信號量爲2,而等待的人員相當於等待執行的線程,每次只有等待信號量釋放才能讓其他線程進來運行。

2.代碼實現

1.代碼書寫

public class SemaphoreDemo {

    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getId() + "工作結束了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }
                }
            }.start();
        }
    }


}

2.輸出結果

在這裏插入圖片描述
在這裏插入圖片描述

3.源碼閱讀

1.構造函數

這邊有兩個構造函數,默認是非公平的方式實現的,也可以通過設置boolean值來決定使用公平方式還是非公平的方式.不多贅述.

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

2.請求鎖:acquire

這邊獲取的是共享鎖,調用AQS的實現。具體來看一下tryAcquire這個方法

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        //線程本身就是阻塞狀態報錯
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }
        if (tryAcquireShared(arg) < 0) {
            //嘗試獲取共享鎖失敗,這邊的嘗試是直接返回狀態時候爲0的結果,如果不爲0表示獲取失敗
            doAcquireSharedInterruptibly(arg);
        }
    }

1.嘗試獲取鎖:tryAcquire

通過獲取當前的state,減去需要的共享鎖數量,通過剩餘的共享鎖數量表示是否需要等待,這邊有幾種情況:
1.如果結果<0.表示當前沒有可用的信號量,必須要加入到共享隊列中進行等待
2.如果CAS設置非0的值成功,則表示當前已經獲取到鎖,不需要加入到共享隊列.


        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                //state - acquire得到當前剩餘可用的信號量
                int available = getState();
                int remaining = available - acquires;

                //小於0表示沒有可用資源,需要加入到等待隊列中,否則通過CAS設置成功表示當前奪鎖成功,可以運行了。
                if (remaining < 0 || compareAndSetState(available, remaining)){
                    return remaining;
                }
            }
        }

2.共享隊列等待:doAcquireSharedInterruptibly

這邊同樣需要調用tryAcquire方法來判斷是否可以獲取到運行權限,如果信號量任然小於0,則當前的線程會一直等待,直到有可用的信號量才能執行線程方法。

    private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
        //將當前線程添加到隊列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            //死循環判斷是否頭節點,頭結點則獲取鎖,
            for (; ; ) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        // help GC
                        p.next = null;
                        failed = false;
                        return;
                    }
                }

                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
                    throw new InterruptedException();
                }
            }
        } finally {
            if (failed) {
                cancelAcquire(node);
            }
        }
    }

2.釋放鎖:release

release方法發這邊直接調用AQS的releaseShared方法。

    public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

1.嘗試釋放:tryReleaseShared

通過死循環去釋放鎖,這邊一定會返回true結果,使用循環CAS方式.

        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                // overflow
                //下一個小於當前,說明不對,直接拋錯,如果傳進來的是負數,有可能出現這種情況
                if (next < current) {
                    throw new Error("Maximum permit count exceeded");
                }
                //通過CAS方式修改state成功返回true。這是一個死循環,所以最後一定會成功返回的.
                //也就是說當前持有的信號量一定會被釋放掉.
                if (compareAndSetState(current, next)){
                    return true;
                }
            }
        }

2.解鎖:doReleaseShared

這邊是 釋放頭結點(通過死循環保證一定被釋放),並設置爲傳播態。

 private void doReleaseShared() {
        for (; ; ) {
            //進入死循環
            Node h = head;
            //如果有隊列有節點等待
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //如果waitState是SIGNAL
                if (ws == Node.SIGNAL) {
                    //設置waitstate 爲0失敗則continue重新開始循環
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) {
                        // loop to recheck cases
                        continue;
                    }
                    //如果設置節點waitstate成功,則喚醒下一個節點
                    unparkSuccessor(h);
                }
                //設置爲傳播狀態.
                else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {
                    // loop on failed CAS
                    continue;
                }

            }
            // loop if head changed
            //保證節點被釋放才能結束循環
            if (h == head) {
                break;
            }
        }
    }
發佈了84 篇原創文章 · 獲贊 15 · 訪問量 3211
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章