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

Java中的Semaphore原理解析

 更新時間:2024年01月22日 10:28:32   作者:我不是歐拉_  
這篇文章主要介紹了Java中的Semaphore原理解析,Semaphore(信號量)是用來控制同時訪問特定資源的線程數(shù)量,它通過協(xié)調(diào)各個線程,以保證合理的使用公共資源,需要的朋友可以參考下

1. Semaphore是什么?

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

Semaphore一般用于流量的控制,特別是公共資源有限的應(yīng)用場景。例如數(shù)據(jù)庫的連接,假設(shè)數(shù)據(jù)庫的連接數(shù)上線為10個,多個線程并發(fā)操作數(shù)據(jù)庫可以使用Semaphore來控制并發(fā)操作數(shù)據(jù)庫的線程個數(shù)最多為10個。

2. 類圖

通過類圖可以看到,Semaphore與ReentrantLock的內(nèi)部類的結(jié)構(gòu)相同,類內(nèi)部總共存在Sync、NonfairSync、FairSync三個類,NonfairSync與FairSync類繼承自Sync類,Sync類繼承自AbstractQueuedSynchronizer抽象類。 

3. 實現(xiàn)原理

3.1 使用示例

    // 定義一個資源池類
    class Pool {
        // 可用資源數(shù)100
        private static final int MAX_AVAILABLE = 100;
        // 定義信號量100
        private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
        // 獲取資源
        public Object getItem() throws InterruptedException {
            // 嘗試獲取
            available.acquire();
            // 返回可用資源
            return getNextAvailableItem();
        }
        // 釋放資源
        public void putItem(Object x) {
            // 如果資源標(biāo)記為未被使用
            if (markAsUnused(x))
            // 釋放資源
            available.release();
        }
        // Not a particularly efficient data structure; just for demo
        // 定義資源類型,可以是滿足業(yè)務(wù)的任何類型
        protected Object[] items = new Object[MAX_AVAILABLE] ... whatever kinds of items being managed
        // 是否被使用標(biāo)記
        protected boolean[] used = new boolean[MAX_AVAILABLE];
        // 獲取下一個可用資源
        protected synchronized Object getNextAvailableItem() {
            // 循環(huán)遍歷
            for (int i = 0; i < MAX_AVAILABLE; ++i) {
                // 如果未被使用
                if (!used[i]) {
                    // 使用標(biāo)記設(shè)置為true
                    used[i] = true;
                    // 返回當(dāng)前的資源
                    return items[i];
                }
            }
            return null; // not reached
        }
        // 標(biāo)記資源為未被使用
        protected synchronized boolean markAsUnused(Object item) {
            // 循環(huán)遍歷
            for (int i = 0; i < MAX_AVAILABLE; ++i) {
                // 找到需要釋放的資源
                if (item == items[i]) {
                    // 如果是被使用中
                    if (used[i]) {
                    // 使用標(biāo)記設(shè)置為false
                    used[i] = false;
                    // 返回true表示標(biāo)記成功
                    return true;
                } else
                    // 返回false表示標(biāo)記失敗
                    return false;
                }
            }
            return false;
        }
    }

3.2 Sync

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;
        // 構(gòu)造方法,調(diào)用父類AQS的setState方法,給共享變量state賦值
        // 即通過構(gòu)造方法給鎖的數(shù)量附初始值
        Sync(int permits) {
            setState(permits);
        }
        // 獲取鎖,也叫許可
        final int getPermits() {
            return getState();
        }
        // 共享模式下的非公平獲取
        // 此方法也體現(xiàn)出與ReentrantLock中Sync的實現(xiàn)不同
        // ReentrantLock中Sync是獨占模式下的獲取
        // 具體實現(xiàn)的不同體現(xiàn)在int remaining = available - acquires;
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                // 獲取鎖的可用數(shù)量
                int available = getState();
                // 可用數(shù)量 - 請求的數(shù)量(acquires默認(rèn)值為1) = 剩余量
                int remaining = available - acquires;
                // 如果remaining < 0即請求的鎖大于可用的數(shù)量,馬上返回負數(shù),表示獲取鎖失敗
                if (remaining < 0 ||
                    // 否則通過CAS的方式將可用數(shù)量換成剩余量,并返回剩余量
                    // 自旋 + CAS 保證線程安全,線程不用排隊體現(xiàn)出非公平性
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
        // 共享模式下釋放鎖
        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");
                // CAS修改鎖數(shù)量,成功則返回,失敗則繼續(xù)自旋
                if (compareAndSetState(current, next))
                    return true;
            }
        }
        // 根據(jù)指定的縮減量減小可用鎖的數(shù)目
        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }
        // 獲取并返回立即可用的所有鎖
        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }

3.3 NonfairSync

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;
        // 構(gòu)造方法初始化鎖數(shù)量
        NonfairSync(int permits) {
            super(permits);
        }
        // 直接調(diào)用nonfairTryAcquireShared方法,走非公平策略
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

3.4 FairSync

static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;
        // 構(gòu)造方法初始化鎖數(shù)量
        FairSync(int permits) {
            super(permits);
        }
        // 共享模式下的公平策略獲取
        // 與非公平策略唯一的不同體現(xiàn)在線程是否需要排隊
        // 即是否調(diào)用hasQueuedPredecessors()方法進行判斷
        // 如果需要排隊則立即返回繼續(xù)排隊
        // 否則通過CAS方式獲取鎖并返貨鎖的剩余量,結(jié)束自旋
        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;
            }
        }
    }

通過分析代碼發(fā)現(xiàn),Semaphore與ReentrantLock的內(nèi)部類的結(jié)構(gòu)相同,具體實現(xiàn)的不同體現(xiàn)在 int remaining = available - acquires這行代碼上。

ReentrantLock對于鎖的控制是 int c = getState(); if (c == 0){....}。體現(xiàn)為一種獨占的控制。

Semaphore對鎖的控制是 for (;;) { int available = getState(); int remaining = available - acquires;......}。即所有線程都可以進入自旋,只要鎖有剩余量都可以嘗試獲取鎖,體現(xiàn)為一種共享的控制。

3.5 Semaphore

public class Semaphore implements java.io.Serializable {
    private static final long serialVersionUID = -3222578661600680210L;
    /** All mechanics via AbstractQueuedSynchronizer subclass */
    // 同步隊列
    private final Sync sync;
    // 構(gòu)造方法初始話鎖數(shù)量
    // 默認(rèn)采用非公平策略
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    // 構(gòu)造方法,帶一個布爾參數(shù),true表示采用公平策略,false表示采用非公平策略
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
}

3.5.1 acquire() 方法解析

// Semaphore
public void acquire() throws InterruptedException {
        // 調(diào)用sync的acquireSharedInterruptibly,即響應(yīng)中斷的獲取
        // 因為sync繼承AbstractQueuedSynchronizer
        // 即調(diào)用AQS的acquireSharedInterruptibly
        sync.acquireSharedInterruptibly(1);
    }
// 進入AQS
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        // 如果線程被中斷,則響應(yīng)中斷
        if (Thread.interrupted())
            throw new InterruptedException();
        // 否則調(diào)用tryAcquireShared,如果獲取的鎖小于0即獲取鎖失敗則調(diào)用doAcquireSharedInterruptibly方法,進入同步隊列排隊
        // 如果獲取鎖成功則不排隊,走業(yè)務(wù)邏輯
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
// Semaphore 中tryAcquireShared的實現(xiàn)
// 公平策略
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;
            }
        }
// 非公平策略
protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
// 前面已經(jīng)解析過,不在贅述
 

3.5.2 release() 方法解析

// Semaphore
public void release() {
        sync.releaseShared(1);
    }
// 進入AQS
public final boolean releaseShared(int arg) {
        // 嘗試釋放鎖, 如果釋放鎖成功
        if (tryReleaseShared(arg)) {
            // 線程出同步隊列,返回true
            doReleaseShared();
            return true;
        }
        // 否則返回false
        return false;
    }
// Semaphore 中tryReleaseShared實現(xiàn)
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;
            }
        }

3.5.3 其他方法

方法說明調(diào)用
acquire(int permits)獲取信號量,指定獲取許可的個數(shù),響應(yīng)中斷sync.acquireSharedInterruptibly(permits)
acquireUninterruptibly()獲取信號量,默認(rèn)獲取1個許可,不響應(yīng)中斷sync.acquireShared(1)
acquireUninterruptibly(int permits)獲取信號量,指定獲取許可的個數(shù),不響應(yīng)中斷sync.acquireShared(permits)
release(int permits)釋放信號量,指定釋放許可的個數(shù)sync.releaseShared(permits);
tryAcquire()嘗試獲取許可,如果獲取成功返回true,否則返回false,不會阻塞線程,而且不響應(yīng)中斷sync.nonfairTryAcquireShared(1)
tryAcquire(int permits)同上,可以指定獲取許可的個數(shù)sync.nonfairTryAcquireShared(permits)
tryAcquire(long timeout, TimeUnit unit)共享式超時獲取sync.tryAcquireSharedNanos(1, unit.toNanos(timeout))
tryAcquire(int permits, long timeout, TimeUnit unit)同上,可以指定獲取許可的個數(shù)sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout))
availablePermits()獲取可用許可數(shù)sync.getPermits()
drainPermits()將剩下的信號量一次性消耗光,并且返回所消耗的信號量sync.drainPermits()
reducePermits(int reduction)減少信號量的總數(shù),不會導(dǎo)致任何線程阻塞,調(diào)用該方法可能會導(dǎo)致信號量最終為負數(shù)sync.reducePermits(reduction)
isFair()是否采用公平策略
hasQueuedThreads()是否是已排隊的線程
getQueueLength()獲取排隊線程的長度
getQueuedThreads()獲取排隊線程

4. 總結(jié)

Semaphore是一個有效的流量控制工具,它基于AQS共享鎖實現(xiàn)。我們常常用它來控制對有限資源的訪問。使用步驟

每次使用資源前,先申請一個信號量,如果資源數(shù)不夠,就會阻塞等待;每次釋放資源后,就釋放一個信號量。

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

相關(guān)文章

  • Java淺析枚舉類的使用

    Java淺析枚舉類的使用

    枚舉類型可以取代以往常量的定義方式,即將常量封裝在類或接口中。此外,枚舉類型還提供了安全檢查功能。本文就來和大家講講Java中枚舉類的用法,需要的可以參考一下
    2022-07-07
  • Java技術(shù)匯總

    Java技術(shù)匯總

    本篇文章主要對Java基本知識點和技術(shù)點的一些看法和介紹,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-03-03
  • mybatis group by substr函數(shù)傳參報錯的解決

    mybatis group by substr函數(shù)傳參報錯的解決

    這篇文章主要介紹了mybatis group by substr函數(shù)傳參報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • SpringBoot中POST請求參數(shù)校驗的實戰(zhàn)指南

    SpringBoot中POST請求參數(shù)校驗的實戰(zhàn)指南

    在現(xiàn)代的Web開發(fā)中,數(shù)據(jù)校驗是確保應(yīng)用程序穩(wěn)定性和安全性的關(guān)鍵環(huán)節(jié),Spring Boot提供了強大而靈活的校驗機制,能夠幫助開發(fā)者輕松地對POST請求參數(shù)進行校驗,本文將詳細介紹如何在Spring Boot中實現(xiàn)POST請求參數(shù)的校驗,需要的朋友可以參考下
    2025-07-07
  • SWT(JFace) FTP客戶端實現(xiàn)

    SWT(JFace) FTP客戶端實現(xiàn)

    SWT(JFace)小制作:FTP客戶端實現(xiàn)
    2009-06-06
  • 一文帶你深入了解SpringBoot參數(shù)配置的詳細步驟

    一文帶你深入了解SpringBoot參數(shù)配置的詳細步驟

    SpringBoot應(yīng)用就像一個超級智能的變形金剛,而參數(shù)配置就是你手里的萬能遙控器,下面小編就和大家詳細介紹一下SpringBoot參數(shù)配置的相關(guān)應(yīng)用,感興趣的小伙伴可以了解下
    2025-12-12
  • java實現(xiàn)上傳和下載工具類

    java實現(xiàn)上傳和下載工具類

    這篇文章主要為大家詳細介紹了java實現(xiàn)上傳和下載工具類,文件上傳到ftp服務(wù)工具類,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Java實現(xiàn)局域網(wǎng)聊天小程序

    Java實現(xiàn)局域網(wǎng)聊天小程序

    這篇文章主要為大家詳細介紹了Java實現(xiàn)局域網(wǎng)聊天小程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java超詳細分析垃圾回收機制

    Java超詳細分析垃圾回收機制

    一個運行中的程序,?產(chǎn)生的對象是大量的,?如果對象不被繼續(xù)使用,?就會成為垃圾,?最后越堆越多,?最后占滿內(nèi)存,?所以我們要對這些垃圾進行回收,保持程序的正常運行
    2022-05-05
  • Springboot之修改啟動端口的兩種方式(小結(jié))

    Springboot之修改啟動端口的兩種方式(小結(jié))

    這篇文章主要介紹了Springboot之修改啟動端口的兩種方式(小結(jié)),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評論

和静县| 蒲城县| 沾益县| 宕昌县| 蕲春县| 开化县| 若羌县| 乡宁县| 潜山县| 丹阳市| 芦溪县| 曲水县| 大余县| 融水| 铁岭市| 阿荣旗| 溆浦县| 廊坊市| 乐陵市| 南部县| 平定县| 呼伦贝尔市| 德化县| 确山县| 陕西省| 炎陵县| 旌德县| 环江| 卢氏县| 安乡县| 阜新| 惠来县| 永修县| 博客| 四会市| 临湘市| 乃东县| 洪洞县| 登封市| 中西区| 延寿县|