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

Java的Semaphore信號量使用及原理解析

 更新時間:2023年12月26日 08:34:16   作者:wrr-cat  
這篇文章主要介紹了Java的Semaphore信號量使用及原理解析,Semaphore 通常我們叫它信號量, 可以用來控制同時訪問特定資源的線程數(shù)量,通過協(xié)調(diào)各個線程,以保證合理的使用資源,需要的朋友可以參考下

1、Semaphore 是什么

Semaphore 通常我們叫它信號量, 可以用來控制同時訪問特定資源的線程數(shù)量,通過協(xié)調(diào)各個線程,以保證合理的使用資源。

可以把它簡單的理解成我們停車場入口立著的那個顯示屏,每有一輛車進(jìn)入停車場顯示屏就會顯示剩余車位減1,每有一輛車從停車場出去,顯示屏上顯示的剩余車輛就會加1,當(dāng)顯示屏上的剩余車位為0時,停車場入口的欄桿就不會再打開,車輛就無法進(jìn)入停車場了,直到有一輛車從停車場出去為止。

2、使用場景

朱勇用于那些資源有明確訪問數(shù)量限制的場景,常用于限流 。

比如:數(shù)據(jù)庫連接池,同時進(jìn)行連接的線程有數(shù)量限制,連接不能超過一定的數(shù)量,當(dāng)連接達(dá)到了限制數(shù)量后,后面的線程只能排隊等前面的線程釋放了數(shù)據(jù)庫連接才能獲得數(shù)據(jù)庫連接。

比如:停車場場景,車位數(shù)量有限,同時只能容納多少臺車,車位滿了之后只有等里面的車離開停車場外面的車才可以進(jìn)入。

3、Semaphore常用方法說明

信號量的構(gòu)造函數(shù) 非公平:

public Semaphore(int permits);//permits就是允許同時運(yùn)行的線程數(shù)目

公平(獲得鎖的順序與線程啟動順序有關(guān)):

public Semaphore(int permits,boolean fair);//permits就是允許同時運(yùn)行的線程數(shù)目
acquire()  
獲取一個令牌,在獲取到令牌、或者被其他線程調(diào)用中斷之前線程一直處于阻塞狀態(tài)。
?
acquire(int permits)  
獲取一個令牌,在獲取到令牌、或者被其他線程調(diào)用中斷、或超時之前線程一直處于阻塞狀態(tài)。
acquireUninterruptibly() 
獲取一個令牌,在獲取到令牌之前線程一直處于阻塞狀態(tài)(忽略中斷)。
tryAcquire()
嘗試獲得令牌,返回獲取令牌成功或失敗,不阻塞線程。
?
tryAcquire(long timeout, TimeUnit unit)
嘗試獲得令牌,在超時時間內(nèi)循環(huán)嘗試獲取,直到嘗試獲取成功或超時返回,不阻塞線程。
?
release()
釋放一個令牌,喚醒一個獲取令牌不成功的阻塞線程。
?
hasQueuedThreads()
等待隊列里是否還存在等待線程。
?
getQueueLength()
獲取等待隊列里阻塞的線程數(shù)。
?
drainPermits()
清空令牌把可用令牌數(shù)置為0,返回清空令牌的數(shù)量。
?
availablePermits()
返回可用的令牌數(shù)量。

4、用semaphore 實現(xiàn)停車場提示牌功能。

每個停車場入口都有一個提示牌,上面顯示著停車場的剩余車位還有多少,當(dāng)剩余車位為0時,不允許車輛進(jìn)入停車場,直到停車場里面有車離開停車場,這時提示牌上會顯示新的剩余車位數(shù)。

業(yè)務(wù)場景 :

1、停車場容納總停車量10。

2、當(dāng)一輛車進(jìn)入停車場后,顯示牌的剩余車位數(shù)響應(yīng)的減1.

3、每有一輛車駛出停車場后,顯示牌的剩余車位數(shù)響應(yīng)的加1。

4、停車場剩余車位不足時,車輛只能在外面等待。

代碼:

public class TestCar {
?
    //停車場同時容納的車輛10
    private  static  Semaphore semaphore=new Semaphore(10);
?
    public static void main(String[] args) {
?
        //模擬100輛車進(jìn)入停車場
        for(int i=0;i<100;i++){
?
            Thread thread=new Thread(new Runnable() {
                public void run() {
                    try {
                        System.out.println("===="+Thread.currentThread().getName()+"來到停車場");
                        if(semaphore.availablePermits()==0){
                            System.out.println("車位不足,請耐心等待");
                        }
                        semaphore.acquire();//獲取令牌嘗試進(jìn)入停車場
                        System.out.println(Thread.currentThread().getName()+"成功進(jìn)入停車場");
                        Thread.sleep(new Random().nextInt(10000));//模擬車輛在停車場停留的時間
                        System.out.println(Thread.currentThread().getName()+"駛出停車場");
                        semaphore.release();//釋放令牌,騰出停車場車位
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },i+"號車");
?
            thread.start();
?
        }
?
    }
}
?

5、Semaphore實現(xiàn)原理

(1)、Semaphore初始化

Semaphore semaphore=new Semaphore(2);

1、當(dāng)調(diào)用new Semaphore(2) 方法時,默認(rèn)會創(chuàng)建一個非公平的鎖的同步阻塞隊列。

2、把初始令牌數(shù)量賦值給同步隊列的state狀態(tài),state的值就代表當(dāng)前所剩余的令牌數(shù)量。

初始化完成后同步隊列信息如下圖:

(2)獲取令牌

semaphore.acquire();

1、當(dāng)前線程會嘗試去同步隊列獲取一個令牌,獲取令牌的過程也就是使用原子的操作去修改同步隊列的state ,獲取一個令牌則修改為state=state-1。

2、 當(dāng)計算出來的state<0,則代表令牌數(shù)量不足,此時會創(chuàng)建一個Node節(jié)點加入阻塞隊列,掛起當(dāng)前線程。

3、當(dāng)計算出來的state>=0,則代表獲取令牌成功。

源碼:

/**
     *  獲取1個令牌
     */
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
/**
     * 共享模式下獲取令牌,獲取成功則返回,失敗則加入阻塞隊列,掛起線程
     * @param arg
     * @throws InterruptedException
     */
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //嘗試獲取令牌,arg為獲取令牌個數(shù),當(dāng)可用令牌數(shù)減當(dāng)前令牌數(shù)結(jié)果小于0,則創(chuàng)建一個節(jié)點加入阻塞隊列,掛起當(dāng)前線程。
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
/**
     * 1、創(chuàng)建節(jié)點,加入阻塞隊列,
     * 2、重雙向鏈表的head,tail節(jié)點關(guān)系,清空無效節(jié)點
     * 3、掛起當(dāng)前節(jié)點線程
     * @param arg
     * @throws InterruptedException
     */
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //創(chuàng)建節(jié)點加入阻塞隊列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                //獲得當(dāng)前節(jié)點pre節(jié)點
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);//返回鎖的state
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //重組雙向鏈表,清空無效節(jié)點,掛起當(dāng)前線程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

線程1、線程2、線程3、分別調(diào)用semaphore.acquire(),整個過程隊列信息變化如下圖:

(3)、釋放令牌

 semaphore.release();

當(dāng)調(diào)用semaphore.release() 方法時

1、線程會嘗試釋放一個令牌,釋放令牌的過程也就是把同步隊列的state修改為state=state+1的過程

2、釋放令牌成功之后,同時會喚醒同步隊列的所有阻塞節(jié)共享節(jié)點線程

3、被喚醒的節(jié)點會重新嘗試去修改state=state-1 的操作,如果state>=0則獲取令牌成功,否則重新進(jìn)入阻塞隊列,掛起線程。

源碼:

 /**
     * 釋放令牌
     */
    public void release() {
        sync.releaseShared(1);
    }
/**
     *釋放共享鎖,同時喚醒所有阻塞隊列共享節(jié)點線程
     * @param arg
     * @return
     */
    public final boolean releaseShared(int arg) {
        //釋放共享鎖
        if (tryReleaseShared(arg)) {
            //喚醒所有共享節(jié)點線程
            doReleaseShared();
            return true;
        }
        return false;
    }
 /**
     * 喚醒所有共享節(jié)點線程
     */
    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {//是否需要喚醒后繼節(jié)點
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))//修改狀態(tài)為初始0
                        continue;
                    unparkSuccessor(h);//喚醒h.nex節(jié)點線程
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE));
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

繼上面的圖,當(dāng)我們線程1調(diào)用semaphore.release(); 時候整個流程如下圖:

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

相關(guān)文章

  • 淺析Java中SimpleDateFormat為什么是線程不安全的

    淺析Java中SimpleDateFormat為什么是線程不安全的

    SimpleDateFormat是Java中用于日期時間格式化的一個類,它提供了對日期的解析和格式化能力,本文主要來和大家一起探討一下SimpleDateFormat為什么是線程不安全的,感興趣的可以了解下
    2024-02-02
  • springcloud整合gateway實現(xiàn)網(wǎng)關(guān)的示例代碼

    springcloud整合gateway實現(xiàn)網(wǎng)關(guān)的示例代碼

    本文主要介紹了springcloud整合gateway實現(xiàn)網(wǎng)關(guān)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 在Java中實現(xiàn)線程安全的單例模式的常見方式

    在Java中實現(xiàn)線程安全的單例模式的常見方式

    單例模式是一種常用的軟件設(shè)計模式,它確保一個類只有一個實例,并提供一個全局訪問點,在多線程環(huán)境下,確保單例模式的線程安全性是非常重要的,因為多個線程可能會同時嘗試創(chuàng)建實例,導(dǎo)致實例不唯一的問題,本文介紹了在Java中實現(xiàn)線程安全的單例模式有幾種常見的方式
    2024-09-09
  • controller?&?service?&?dao之間的關(guān)系及解讀

    controller?&?service?&?dao之間的關(guān)系及解讀

    本文介紹了軟件開發(fā)中Controller、Service、Mapper和Entity層的概念及其關(guān)系,類比于餐廳工作流程解釋各層職責(zé),Entity層定義數(shù)據(jù)結(jié)構(gòu),Service層處理業(yè)務(wù)邏輯,Mapper層與數(shù)據(jù)庫交互,Controller層接收用戶請求,同時提供了Eclipse和IDEA中自動生成方法的快捷操作說明
    2026-04-04
  • 5種Java經(jīng)典創(chuàng)建型模式詳解

    5種Java經(jīng)典創(chuàng)建型模式詳解

    這篇文章主要為大家詳細(xì)介紹了5種Java經(jīng)典創(chuàng)建型模式,感興趣的小伙伴們可以參考一下
    2016-03-03
  • Java CAS原子操作詳解

    Java CAS原子操作詳解

    在synchronized的優(yōu)化過程中我們看到大量使用了CAS操作,CAS全稱Compare And Set(或Compare And Swap),簡單來說CAS操作就是一個虛擬機(jī)實現(xiàn)的原子操作
    2023-02-02
  • Java面向?qū)ο筇匦陨钊肱傥龇庋b

    Java面向?qū)ο筇匦陨钊肱傥龇庋b

    封裝是一個非常廣泛的概念,小到一個屬性的封裝,大到一個框架或者一個項目的封裝,下面這篇文章主要給大家介紹了關(guān)于java中封裝的那點事,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • springboot中使用okhttp3的小結(jié)

    springboot中使用okhttp3的小結(jié)

    OkHttp3是一個Java HTTP客戶端,可以處理各種請求類型,比如 GET、POST、PUT 等,并且支持高效的 HTTP 連接池、請求和響應(yīng)緩存、以及異步請求處理等,感興趣的可以了解一下
    2025-09-09
  • spring boot security設(shè)置忽略地址不生效的解決

    spring boot security設(shè)置忽略地址不生效的解決

    這篇文章主要介紹了spring boot security設(shè)置忽略地址不生效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 談?wù)刯ava的concurrent用法

    談?wù)刯ava的concurrent用法

    這篇文章給大家介紹java的concurrent用法,感興趣的朋友一起學(xué)習(xí)吧
    2015-10-10

最新評論

望奎县| 双牌县| 汝城县| 固镇县| 会东县| 津南区| 赤峰市| 金昌市| 晋中市| 灌南县| 尤溪县| 黄大仙区| 宝应县| 邵东县| 海林市| 扬州市| 中宁县| 西宁市| 镇坪县| 青神县| 洛南县| 苗栗县| 荣成市| 花垣县| 郎溪县| 武安市| 土默特右旗| 玛曲县| 冕宁县| 吴江市| 新竹县| 湖口县| 天等县| 宿迁市| 苗栗市| 包头市| 济南市| 台北市| 河曲县| 阆中市| 高淳县|