Semaphore 原理分析

分析下SemaPhore吧,也是基于AQS實現的,對并發進行控制的工具類,看下其怎么實現的,

    Semaphore semaphore = new Semaphore(3);semaphore.acquire();semaphore.release();

Semaphore 常用于控制并發量,比如這里設置為3,就可以只有三個線程可以acquire拿到資源,后續來的線程需要排隊,等原有線程release釋放之后,才可以接入新的請求,用于控制最大并發。

acquire 實現

// 默認非公平的
public Semaphore(int permits) {sync = new NonfairSync(permits);
}
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)doAcquireSharedInterruptibly(arg);
}
// 執行的AQS的獲取資源
private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {// 添加共享節點final Node node = addWaiter(Node.SHARED);try {for (;;) {// 死循環判斷,park之后喚醒,還是走這里final Node p = node.predecessor();// 如果前面是頭節點的話if (p == head) {// 執行的子類實現的嘗試方法int r = tryAcquireShared(arg);// 獲取成功的話if (r >= 0) {// 對其進行喚醒setHeadAndPropagate(node, r);p.next = null; // help GCreturn;}}// 如果不是頭節點,判斷需要park不,前節點是signal就進行park// park之前檢查是不是被打斷// 如果第一次不是,會給前節點設置signal,然后下一次再循環到,就park了if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} catch (Throwable t) {cancelAcquire(node);throw t;}
}// 實際獲取到鎖之后,改頭,然后傳播,這里是不是傳播根據子類返回的是0還是大于0private void setHeadAndPropagate(Node node, int propagate) {Node h = head; // Record old head for check belowsetHead(node);// 大于0,頭節點為空(執行完了),狀態小于0,// 新的頭節點(當前節點)為空,或者狀態小于0if (propagate > 0 || h == null || h.waitStatus < 0 ||(h = head) == null || h.waitStatus < 0) {Node s = node.next;//如果有后節點為空或者是共享的,釋放if (s == null || s.isShared())doReleaseShared();}}private void doReleaseShared() {for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;// 這里會先把狀態改為0,改成功了會是釋放,成功釋放之后if (ws == Node.SIGNAL) {if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))continue;            // loop to recheck casesunparkSuccessor(h);}// 如果為0 改為傳播else if (ws == 0 &&!h.compareAndSetWaitStatus(0, Node.PROPAGATE))continue;                // loop on failed CAS}// 判斷等于頭,就是沒改變頭就breakif (h == head)                   // loop if head changedbreak;}}

可以看到這是在獲取資源,獲取不到的時候進入隊列等待,默認的是非公平的,去看下怎么實現的

Sync

Semaphore 內部類Sync實現了AQS,看下怎么實現的

abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 1192457210091910933L;// 初始設置的資源數,也是通過stateSync(int permits) {setState(permits);}final int getPermits() {return getState();}// 非公平的獲取資源final int nonfairTryAcquireShared(int acquires) {for (;;) {// 獲取可用的資源int available = getState();// 如果可用的小于需要獲取的int remaining = available - acquires;// 小于0直接返回了,如果不小于0,就cas設置,設置成功就返回對應的值了大于等于0的if (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}// 釋放資源protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();// 給對應的數量加上釋放的數量int next = current + releases;// 釋放的不能為負數,也不能超過限制if (next < current) // overflowthrow new Error("Maximum permit count exceeded");// cas設置,成功返回釋放完成if (compareAndSetState(current, next))return true;}}// 減去對應的statefinal void reducePermits(int reductions) {for (;;) {int current = getState();int next = current - reductions;if (next > current) // underflowthrow new Error("Permit count underflow");if (compareAndSetState(current, next))return;}}final int drainPermits() {for (;;) {// 是不是為0,不為0的時候嘗試設置為0int current = getState();if (current == 0 || compareAndSetState(current, 0))return current;}}
}
// 看下對應的公平鎖實現,非公平直接使用Sync的方法獲取
static final class FairSync extends Sync {private static final long serialVersionUID = 2014338818796000944L;FairSync(int permits) {super(permits);}protected int tryAcquireShared(int acquires) {for (;;) {// 是不是有在等待的,有就返回-1了,差的就是這個判斷if (hasQueuedPredecessors())return -1;int available = getState();int remaining = available - acquires;if (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}
}

可以看到Sync繼承AQS之后實現的獲取資源方法就是對對應的state進行減,確保其大于等于0,有就可以獲取,公平非公平的實現就是判斷喜愛是不是有在等待的,有的話直接返回-1,不進行嘗試。

release

public void release() {sync.releaseShared(1);
}public final boolean releaseShared(int arg) {// 先改,成功就實際釋放if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;
}
protected final boolean tryReleaseShared(int releases) {for (;;) {// 改了state的值int current = getState();int next = current + releases;if (next < current) // overflowthrow new Error("Maximum permit count exceeded");if (compareAndSetState(current, next))return true;}
}
private void doReleaseShared() {for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;// 喚醒后面if (ws == Node.SIGNAL) {if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))continue;            // loop to recheck cases// 實際喚醒線程unparkSuccessor(h);}else if (ws == 0 &&!h.compareAndSetWaitStatus(0, Node.PROPAGATE))continue;                // loop on failed CAS}if (h == head)                   // loop if head changedbreak;}
}// 喚醒線程
private void unparkSuccessor(Node node) {/** If status is negative (i.e., possibly needing signal) try* to clear in anticipation of signalling.  It is OK if this* fails or if status is changed by waiting thread.*/int ws = node.waitStatus;if (ws < 0)node.compareAndSetWaitStatus(ws, 0);// 獲取下一個節點,不為空的時候直接喚醒Node s = node.next;// 如果是空或者取消狀態的話if (s == null || s.waitStatus > 0) {s = null;// 從后向前遍歷,然后喚醒,這里喚醒之后應該去繼續拿資源for (Node p = tail; p != node && p != null; p = p.prev)if (p.waitStatus <= 0)s = p;}if (s != null)LockSupport.unpark(s.thread);
}

總結

簡單總結下吧,Semaphore 通過AQS的state來控制并發數量,也分為公平和非公平,但是使用的是共享鎖,這樣就能根據數量進行喚醒,AQS提供的方法tryAcquire 讓子類實現的,返回正數代表可以繼續向后喚醒,返回0自己得到資源可以執行,就通過這樣的形式來控制并發

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36598.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36598.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36598.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

請教電路高手幫忙Review一下是否可行?

想要實現STM32 3.3V GPIO 控制5V電源通斷&#xff0c;默認狀態為&#xff1a;接通。 使用如下電路圖有無問題&#xff1f;參數是否需要調整&#xff1f;

8.14 ARM

1.練習一 .text 文本段 .global _start 聲明一個_start函數入口 _start: _start標簽&#xff0c;相當于C語言中函數mov r0,#0x2mov r1,#0x3cmp r0,r1beq stopsubhi r0,r0,r1subcc r1,r1,r0stop: stop標簽&#xff0c;相當于C語言中函數b stop 跳轉到stop標簽下的第一條…

C++的IO流

C語言的輸入與輸出 C語言中我們用到的最頻繁的輸入輸出方式就是scanf ()與printf()。 scanf(): 從標準輸入設備(鍵盤)讀取數據&#xff0c;并將值存放在變量中。printf(): 將指定的文字/字符串輸出到標準輸出設備(屏幕)。注意寬度輸出和精度輸出控制。C語言借助了相應的緩沖區來…

javaScript:如何獲取html中的元素對象

目錄 前言&#xff1a; 方法 1.通過id獲取元素 2.通過標簽名獲取元素 3.通過類名class獲取元素 獲取body的方法 1.document.getElementsByTagName(body)[0] 2.document.body 相關代碼 前言&#xff1a; 通過獲取HTML中的元素對象&#xff0c;JavaScript可以對網頁進行動…

學生成績管理系統V1.0

某班有最多不超過30人&#xff08;具體人數由鍵盤輸入&#xff09;參加某門課程的考試&#xff0c;用一維數組作函數參數編程實現如下學生成績管理&#xff1a; &#xff08;1&#xff09;錄入每個學生的學號和考試成績&#xff1b; &#xff08;2&#xff09;計算課程的總分…

Vue [Day7]

文章目錄 自定義創建項目ESlint 代碼規范vuex 概述創建倉庫向倉庫提供數據使用倉庫中的數據通過store直接訪問通過輔助函數 mapState&#xff08;簡化&#xff09;mutations傳參語法(同步實時輸入&#xff0c;實時更新輔助函數 mapMutationsaction &#xff08;異步輔助函數map…

IntelliJ IDEA 2021/2022關閉雙擊shift全局搜索

我這里演示的是修改&#xff0c;刪除是右鍵的時候選擇Remove就好了 IDEA左上角 File-->Settings 找到Navigate -->Search Everywhere &#xff0c;右鍵添加快捷鍵。 OK --> Apply應用

C語言學習之const關鍵字的使用

const修飾變量&#xff1a;const關鍵字修飾變量時&#xff0c;該變量表示是一個只讀變量&#xff0c;不能通過變量名修改變量的值&#xff1b;案例&#xff1a; const int m 20; int const n 30; m 30;//不可以賦值&#xff0c;因為m是只讀變量 n 20;//不可以賦值&#xf…

初始多線程

目錄 認識線程 線程是什么&#xff1a; 線程與進程的區別 Java中的線程和操作系統線程的關系 創建線程 繼承Thread類 實現Runnable接口 其他變形 Thread類及其常見方法 Thread的常見構造方法 Thread類的幾個常見屬性 Thread類常用的方法 啟動一個線程-start() 中斷…

前端食堂技術周刊第 93 期:7 月登陸 Web 平臺的新功能、Node.js 工具箱、Nuxt3 開發技巧、MF 重構方案

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;橙橙冰萃美式 食堂技術周刊倉庫地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童歐巴。歡迎來到前端食堂技術周刊&#xff0c;我們先來…

Android多屏幕支持-Android12

Android多屏幕支持-Android12 1、概覽及相關文章2、屏幕窗口配置2.1 配置xml文件2.2 DisplayInfo#uniqueId 屏幕標識2.3 adb查看信息 3、配置文件解析3.1 xml字段讀取3.2 簡要時序圖 4、每屏幕焦點 android12-release 1、概覽及相關文章 AOSP > 文檔 > 心主題 > 多屏…

如何利用DeepBook自動做市商(AMM),發揮應用的最大價值

盡管Sui宣布DeepBook作為其首個本地流動性層&#xff0c;即中央限價單簿&#xff08;Central Limit Order Book&#xff0c;CLOB&#xff09;&#xff0c;但自動做市商&#xff08;Automated Market Maker&#xff0c;AMM&#xff09;平臺也可以在Sui上發揮作用。事實上&#x…

理解jvm之對象已死怎么判斷?

目錄 引用計數算法 什么是引用 可達性分析算法&#xff08;用的最多的&#xff09; 引用計數算法 定義&#xff1a;在對象中添加一個引用計數器&#xff0c;每當有一個地方引用它時&#xff0c;計數器值就加一&#xff1b;當引用失效時&#xff0c;計數器值就減一&#xff1…

文件批量改名高手:輕松刪除文件名,僅保留編號!

您是否經常需要對大量文件進行命名調整&#xff1f;是否為繁瑣的手動操作而感到厭煩&#xff1f;現在&#xff0c;我們的智能批量文件改名工具為您提供了一種簡單而高效的解決方案&#xff01;只需幾步操作&#xff0c;您就能輕松刪除原有的文件名&#xff0c;僅保留編號&#…

YOLO相關原理(文件結構、視頻檢測等)

超參數進化(hyperparameter evolution) 超參數進化是一種使用了genetic algorithm&#xff08;GA&#xff09;遺傳算法進行超參數優化的一種方法。 YOLOv5的文件結構 images文件夾內的文件和labels中的文件存在一一對應關系 激活函數&#xff1a;非線性處理單元 activation f…

c#學習路線

文章目錄 .net coreN層架構大項目實戰高性能互聯網項目架構c#高級編程各種主流框架分布式通信SSO單點登錄+權限管理系統實戰N層架構WEB安全ASP.NET MVCNoSQLORM框架c#6和c#7新語法VS插件分享項目管理三層項目實戰三層架構ASP.NET基礎數據庫和ASP.NETADO.NET計算機基礎計算機硬件…

C# 11 中的新增功能

本文內容 泛型屬性泛型數學支持數值 IntPtr 和 UIntPtr字符串內插中的換行符 顯示另外 11 個 C# 11 中增加了以下功能&#xff1a; 原始字符串字面量泛型數學支持泛型屬性UTF-8 字符串字面量字符串內插表達式中的換行符列表模式文件本地類型必需的成員自動默認結構常量 str…

【設計模式】MVC 模式

MVC 模式代表 Model-View-Controller&#xff08;模型-視圖-控制器&#xff09; 模式。這種模式用于應用程序的分層開發。 Model&#xff08;模型&#xff09; - 模型代表一個存取數據的對象或 JAVA POJO。它也可以帶有邏輯&#xff0c;在數據變化時更新控制器。View&#xff…

Linux6.37 Kubernetes 集群調度

文章目錄 計算機系統5G云計算第三章 LINUX Kubernetes 集群調度一、調度約束1.調度過程2.指定調度節點3.親和性1&#xff09;節點親和性2&#xff09;Pod 親和性3&#xff09;鍵值運算關系 4.污點(Taint) 和 容忍(Tolerations)1&#xff09;污點(Taint)2&#xff09;容忍(Toler…

centos搭建k8s

centos搭建k8s環境_centos k8s_進擊的Coders的博客-CSDN博客