最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java AQS信號(hào)量Semaphore的使用

 更新時(shí)間:2023年02月02日 10:00:24   作者:飛奔的小付  
Semaphore來自于JDK1.5的JUC包,直譯過來就是信號(hào)量,被作為一種多線程并發(fā)控制工具來使用。本文將詳解其原理與使用方法,感興趣的可以學(xué)習(xí)一下

一.什么是Semaphore

Semaphore,俗稱信號(hào)量,它是操作系統(tǒng)中PV操作的原語(yǔ)在java的實(shí)現(xiàn),它也是基于AbstractQueuedSynchronizer實(shí)現(xiàn)的。

Semaphore的功能非常強(qiáng)大,大小為1的信號(hào)量就類似于互斥鎖,通過同時(shí)只能有一個(gè)線程獲取信號(hào)量實(shí)現(xiàn)。大小為n(n>0)的信號(hào)量可以實(shí)現(xiàn)限流的功能,它可以實(shí)現(xiàn)只能有n個(gè)線程同時(shí)獲取信號(hào)量。

PV操作是操作系統(tǒng)一種實(shí)現(xiàn)進(jìn)程互斥與同步的有效方法。PV操作與信號(hào)量(S)的處理相關(guān),P表示通過的意思,V表示釋放的意思。用PV操作來管理共享資源時(shí),首先要確保PV操作自身執(zhí)行的正確性。
P操作的主要?jiǎng)幼魇牵?br />①S減1;
②若S減1后仍大于或等于0,則進(jìn)程繼續(xù)執(zhí)行;
③若S減1后小于0,則該進(jìn)程被阻塞后放入等待該信號(hào)量的等待隊(duì)列中,然后轉(zhuǎn)進(jìn)程調(diào)度。
V操作的主要?jiǎng)幼魇牵?br />①S加1;
②若相加后結(jié)果大于0,則進(jìn)程繼續(xù)執(zhí)行;
③若相加后結(jié)果小于或等于0,則從該信號(hào)的等待隊(duì)列中釋放一個(gè)等待進(jìn)程,然后再返回原進(jìn)程繼續(xù)執(zhí)行或轉(zhuǎn)進(jìn)程調(diào)度。

二.Semaphore的使用

構(gòu)造器

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

permits 表示許可證的數(shù)量(資源數(shù))

fair 表示公平性,如果這個(gè)設(shè)為 true 的話,下次執(zhí)行的線程會(huì)是等待最久的線程

常用方法

public void acquire() throws InterruptedException
public boolean tryAcquire()
public void release()
public int availablePermits()
public final int getQueueLength()
public final boolean hasQueuedThreads()
protected void reducePermits(int reduction)

  • acquire() 表示阻塞并獲取許可
  • tryAcquire() 方法在沒有許可的情況下會(huì)立即返回 false,要獲取許可的線程不會(huì)阻塞
  • release() 表示釋放許可
  • int availablePermits():返回此信號(hào)量中當(dāng)前可用的許可證數(shù)。
  • int getQueueLength():返回正在等待獲取許可證的線程數(shù)。
  • boolean hasQueuedThreads():是否有線程正在等待獲取許可證。
  • void reducePermit(int reduction):減少 reduction 個(gè)許可證
  • Collection getQueuedThreads():返回所有等待獲取許可證的線程集合

應(yīng)用場(chǎng)景

可以用于做流量控制,特別是公用資源有限的應(yīng)用場(chǎng)景

限流

/**
     * 實(shí)現(xiàn)一個(gè)同時(shí)只能處理5個(gè)請(qǐng)求的限流器
     */
    private static Semaphore semaphore = new Semaphore(5);
    /**
     * 定義一個(gè)線程池
     */
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 50, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(200));
    public static void exec() {
        try {
            semaphore.acquire(1);
            // 模擬真實(shí)方法執(zhí)行
            System.out.println("執(zhí)行exec方法" );
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            semaphore.release(1);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        {
            for (; ; ) {
                Thread.sleep(100);
                // 模擬請(qǐng)求以10個(gè)/s的速度
                executor.execute(() -> exec());
            }
        }
    }

三.Semaphore源碼分析

主要關(guān)注 Semaphore的加鎖解鎖(共享鎖)邏輯實(shí)現(xiàn),線程競(jìng)爭(zhēng)鎖失敗入隊(duì)阻塞邏輯和獲取鎖的線程釋放鎖喚醒阻塞線程競(jìng)爭(zhēng)鎖的邏輯實(shí)現(xiàn)

	public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
            //嘗試獲取共享鎖,大于等于0則直接獲取鎖成功,小于0時(shí)則共享鎖阻塞
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

tryAcquireShared的實(shí)現(xiàn)

   final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                // 當(dāng)減一之后的值小于0 或者 
                // compareAndSetState成功,把state變?yōu)閞emaining,即將狀態(tài)減一
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

入隊(duì)阻塞

 private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //入隊(duì),創(chuàng)建節(jié)點(diǎn) 使用共享模式
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
            //獲取當(dāng)前節(jié)點(diǎn)的前軀節(jié)點(diǎn)
                final Node p = node.predecessor();
                //如果節(jié)點(diǎn)為head節(jié)點(diǎn)
                if (p == head) {
                	//阻塞動(dòng)作比較重,通常會(huì)再嘗試獲取資源,沒有獲取到返回負(fù)數(shù)
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //判斷是否可以阻塞
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

入隊(duì)操作

private Node addWaiter(Node mode) {
		//構(gòu)建節(jié)點(diǎn),模式是共享模式
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {
        	//設(shè)置前一節(jié)點(diǎn)為tail 
            node.prev = pred;
            //設(shè)置當(dāng)前節(jié)點(diǎn)為尾節(jié)點(diǎn)
            if (compareAndSetTail(pred, node)) {
            // 前一節(jié)點(diǎn)的next為當(dāng)前節(jié)點(diǎn)
                pred.next = node;
                return node;
            }
        }
        //創(chuàng)建隊(duì)列
        enq(node);
        return node;
    }

創(chuàng)建隊(duì)列,經(jīng)典的for循環(huán)創(chuàng)建雙向鏈表

 private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { 
            	//節(jié)點(diǎn)為空 則new一個(gè)節(jié)點(diǎn) 設(shè)置頭節(jié)點(diǎn)
                if (compareAndSetHead(new Node()))
                //把這個(gè)節(jié)點(diǎn)的頭節(jié)點(diǎn)賦值給尾節(jié)點(diǎn)
                    tail = head;
            } else {
            // 如果尾節(jié)點(diǎn)存在 就將該節(jié)點(diǎn)的前節(jié)點(diǎn)指向tail
                node.prev = t;
                //設(shè)置當(dāng)前節(jié)點(diǎn)為tail
                if (compareAndSetTail(t, node)) {
                //前一個(gè)節(jié)點(diǎn)的next指向當(dāng)前節(jié)點(diǎn),入隊(duì)操作就完成了
                    t.next = node;
                    return t;
                }
            }
        }
    }

設(shè)置waitStatus狀態(tài)及獲取waitStatus狀態(tài),waitStatus為-1時(shí)可以喚醒后續(xù)節(jié)點(diǎn)

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * 狀態(tài)是-1 就可以喚醒后續(xù)節(jié)點(diǎn)
             *
             */
            return true;
        if (ws > 0) {
            /*
             * 前置任務(wù)已取消,刪掉節(jié)點(diǎn)
             *
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * cas設(shè)置waitstatus狀態(tài),設(shè)置為-1
             * 
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

阻塞 調(diào)用LockSupport.park

 private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

釋放鎖的邏輯

	public void release() {
        sync.releaseShared(1);
    }
	public final boolean releaseShared(int arg) {
		//cas成功則進(jìn)行釋放共享鎖
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

state狀態(tài)+1操作,cas成功,返回true

		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;
            }
        }
private void doReleaseShared() {
        for (;;) {
            Node h = head;
            //頭節(jié)點(diǎn)不為空并且不是尾節(jié)點(diǎn)
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //waitstatus為-1
                if (ws == Node.SIGNAL) {
                //將SIGNAL置為0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;         
                    //喚醒   
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;               
            }
            if (h == head)                  
                break;
        }
    }

喚醒操作

 private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        //ws小于0就將其設(shè)置為0
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        //當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)為空或者ws大于0
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //s不為空 則進(jìn)行喚醒
        if (s != null)
            LockSupport.unpark(s.thread);
    }

喚醒下一個(gè)線程之后,要把上一個(gè)節(jié)點(diǎn)移除隊(duì)列

 private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                //下一個(gè)線程進(jìn)來,如果前置節(jié)點(diǎn)是頭節(jié)點(diǎn),則將前置節(jié)點(diǎn)出隊(duì)
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    //cas獲取資源成功
                    if (r >= 0) {
                    	//出隊(duì)操作
                        setHeadAndPropagate(node, r);
                        //將p.next移除
                        p.next = null;
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

出隊(duì)操作

private void setHeadAndPropagate(Node node, int propagate) {
		//設(shè)置當(dāng)前節(jié)點(diǎn)為head節(jié)點(diǎn),前一節(jié)點(diǎn)的head屬性被刪除
        Node h = head; 
        setHead(node);
        //如果是傳播屬性
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
           //并且是共享模式,可以持續(xù)喚醒下一個(gè),
           //只要資源數(shù)充足 就可以一直往下喚醒,提高并發(fā)量
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
 private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

至此,線程的阻塞喚醒核心邏輯就這么多,共享鎖與獨(dú)占鎖的區(qū)別是可以喚醒后續(xù)的線程,如果資源數(shù)充足的話,可以一直往下喚醒,提高了并發(fā)量。

到此這篇關(guān)于Java AQS信號(hào)量Semaphore的使用的文章就介紹到這了,更多相關(guān)Java信號(hào)量Semaphore內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC執(zhí)行過程詳細(xì)講解

    SpringMVC執(zhí)行過程詳細(xì)講解

    MVC是一種軟件設(shè)計(jì)典范,用一種業(yè)務(wù)邏輯、數(shù)據(jù)、界面顯示分離的方法組織代碼,將業(yè)務(wù)邏輯聚集到一個(gè)組件里面,在改進(jìn)和個(gè)性化定制界面及用戶交互的同時(shí),不需要重新編寫業(yè)務(wù)邏輯,MVC分層有助于管理和架構(gòu)復(fù)雜的應(yīng)用程序
    2022-08-08
  • java中hibernate二級(jí)緩存詳解

    java中hibernate二級(jí)緩存詳解

    本篇文章中主要介紹了java中hibernate二級(jí)緩存詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • Java如何通過反射方式生成數(shù)據(jù)庫(kù)實(shí)體類

    Java如何通過反射方式生成數(shù)據(jù)庫(kù)實(shí)體類

    這篇文章主要介紹了Java如何通過反射方式生成數(shù)據(jù)庫(kù)實(shí)體類問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java如何隨機(jī)生成圖片驗(yàn)證碼

    Java如何隨機(jī)生成圖片驗(yàn)證碼

    這篇文章主要為大家詳細(xì)介紹了Java如何隨機(jī)生成圖片驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • JAVA使用commos-fileupload實(shí)現(xiàn)文件上傳與下載實(shí)例解析

    JAVA使用commos-fileupload實(shí)現(xiàn)文件上傳與下載實(shí)例解析

    這篇文章主要介紹了JAVA使用commos-fileupload實(shí)現(xiàn)文件上傳與下載的相關(guān)資料,需要的朋友可以參考下
    2016-02-02
  • java正則表達(dá)式學(xué)習(xí)筆記之命名捕獲

    java正則表達(dá)式學(xué)習(xí)筆記之命名捕獲

    這篇文章主要為大家詳細(xì)介紹了java正則表達(dá)式中的命名捕獲,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Java實(shí)現(xiàn)簡(jiǎn)單的五子棋游戲示例代碼

    Java實(shí)現(xiàn)簡(jiǎn)單的五子棋游戲示例代碼

    這篇文章主要為大家介紹了如何利用Java語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的五子棋游戲,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java游戲開發(fā)有一定幫助,需要的可以參考一下
    2022-05-05
  • mybaties plus實(shí)體類設(shè)置typeHandler不生效的解決

    mybaties plus實(shí)體類設(shè)置typeHandler不生效的解決

    這篇文章主要介紹了mybaties plus實(shí)體類設(shè)置typeHandler不生效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • java如何讀取超大文件

    java如何讀取超大文件

    這篇文章主要為大家詳細(xì)介紹了java如何讀取超大文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Java解決計(jì)算相鄰兩個(gè)數(shù)的最大差值的問題

    Java解決計(jì)算相鄰兩個(gè)數(shù)的最大差值的問題

    今天給大家?guī)硪坏浪惴}:給定一個(gè)數(shù)組,求如果排序之后,相鄰兩數(shù)的最大差值。要求時(shí)間復(fù)雜度O(N),且要求不能用非基于比較的排序??靵砀S小編一起學(xué)習(xí)一下如何解決這一問題吧
    2021-12-12

最新評(píng)論

东阳市| 东乡族自治县| 延津县| 汉寿县| 柯坪县| 成安县| 金山区| 武功县| 夏邑县| 砚山县| 上栗县| 拉孜县| 宝山区| 博罗县| 射阳县| 若尔盖县| 深圳市| 深州市| 泾阳县| 双鸭山市| 湖北省| 娄烦县| 柳江县| 北辰区| 旌德县| 桐柏县| 吉木乃县| 南雄市| 禹州市| 全南县| 宁海县| 鱼台县| 大同市| 高阳县| 淮安市| 陵水| 鄢陵县| 鲁山县| 古田县| 宁武县| 海南省|